diff options
Diffstat (limited to 'editor/plugins')
45 files changed, 5765 insertions, 3075 deletions
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index c22e1cd88b..b387972558 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -440,7 +440,7 @@ void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource) file->set_current_path(existing); } file->popup_centered_ratio(); - file->set_title(TTR("Save Resource As..")); + file->set_title(TTR("Save Resource As...")); current_option = RESOURCE_SAVE; } @@ -459,6 +459,12 @@ void AnimationPlayerEditor::_animation_remove_confirmed() { Ref<Animation> anim = player->get_animation(current); undo_redo->create_action(TTR("Remove Animation")); + if (player->get_autoplay() == current) { + undo_redo->add_do_method(player, "set_autoplay", ""); + undo_redo->add_undo_method(player, "set_autoplay", current); + // Avoid having the autoplay icon linger around if there is only one animation in the player + undo_redo->add_do_method(this, "_animation_player_changed", player); + } undo_redo->add_do_method(player, "remove_animation", current); undo_redo->add_undo_method(player, "add_animation", current, anim); undo_redo->add_do_method(this, "_animation_player_changed", player); @@ -491,7 +497,7 @@ void AnimationPlayerEditor::_animation_name_edited() { String new_name = name->get_text(); if (new_name == "" || new_name.find(":") != -1 || new_name.find("/") != -1) { - error_dialog->set_text(TTR("ERROR: Invalid animation name!")); + error_dialog->set_text(TTR("Invalid animation name!")); error_dialog->popup_centered_minsize(); return; } @@ -502,7 +508,7 @@ void AnimationPlayerEditor::_animation_name_edited() { } if (player->has_animation(new_name)) { - error_dialog->set_text(TTR("ERROR: Animation name already exists!")); + error_dialog->set_text(TTR("Animation name already exists!")); error_dialog->popup_centered_minsize(); return; } @@ -811,6 +817,8 @@ void AnimationPlayerEditor::_update_player() { play_bw->set_disabled(animlist.size() == 0); play_bw_from->set_disabled(animlist.size() == 0); play_from->set_disabled(animlist.size() == 0); + frame->set_editable(animlist.size() != 0); + animation->set_disabled(animlist.size() == 0); autoplay->set_disabled(animlist.size() == 0); duplicate_anim->set_disabled(animlist.size() == 0); rename_anim->set_disabled(animlist.size() == 0); @@ -820,6 +828,7 @@ void AnimationPlayerEditor::_update_player() { save_anim->set_disabled(animlist.size() == 0); tool_anim->set_disabled(player == NULL); onion_skinning->set_disabled(player == NULL); + pin->set_disabled(player == NULL); int active_idx = -1; for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) { @@ -1011,6 +1020,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) { player->seek_delta(pos, pos - cpos); } else { + player->stop(true); player->seek(pos, true); } @@ -1087,7 +1097,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { case TOOL_COPY_ANIM: { if (!animation->get_item_count()) { - error_dialog->set_text(TTR("ERROR: No animation to copy!")); + error_dialog->set_text(TTR("No animation to copy!")); error_dialog->popup_centered_minsize(); return; } @@ -1102,7 +1112,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard(); if (!anim.is_valid()) { - error_dialog->set_text(TTR("ERROR: No animation resource on clipboard!")); + error_dialog->set_text(TTR("No animation resource on clipboard!")); error_dialog->popup_centered_minsize(); return; } @@ -1133,7 +1143,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { case TOOL_EDIT_RESOURCE: { if (!animation->get_item_count()) { - error_dialog->set_text(TTR("ERROR: No animation to edit!")); + error_dialog->set_text(TTR("No animation to edit!")); error_dialog->popup_centered_minsize(); return; } @@ -1610,7 +1620,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay hb->add_child(load_anim); save_anim = memnew(MenuButton); - save_anim->set_tooltip(TTR("Save the current animation")); + save_anim->set_tooltip(TTR("Save the current animation.")); save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save", TTR("Save")), ANIM_SAVE); save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as", TTR("Save As")), ANIM_SAVE_AS); save_anim->set_focus_mode(Control::FOCUS_NONE); @@ -1678,10 +1688,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay 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_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); + 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_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); - onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS); + onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); + onion_skinning->get_popup()->add_radio_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS); onion_skinning->get_popup()->add_separator(); onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY); onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE); @@ -1690,6 +1700,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay pin = memnew(ToolButton); pin->set_toggle_mode(true); + pin->set_tooltip(TTR("Pin AnimationPlayer")); hb->add_child(pin); resource_edit_anim = memnew(Button); @@ -1717,7 +1728,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay error_dialog = memnew(ConfirmationDialog); error_dialog->get_ok()->set_text(TTR("Close")); - error_dialog->set_text(TTR("Error!")); + error_dialog->set_title(TTR("Error!")); add_child(error_dialog); name_dialog->connect("confirmed", this, "_animation_name_edited"); diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index f0e186e4b0..25582ae0b9 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -578,7 +578,7 @@ void AnimationTreeEditor::_draw_node(const StringName &p_node) { if (anim_tree->animation_node_get_master_animation(p_node) != "") text = anim_tree->animation_node_get_master_animation(p_node); else if (anim.is_null()) - text = "load.."; + text = "load..."; else text = anim->get_name(); @@ -593,7 +593,7 @@ void AnimationTreeEditor::_draw_node(const StringName &p_node) { case AnimationTreePlayer::NODE_TIMESCALE: case AnimationTreePlayer::NODE_TRANSITION: { - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit..", font_color_title); + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); } break; default: editable = false; } @@ -756,6 +756,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { node_popup->clear(); + node_popup->set_size(Size2(1, 1)); node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); @@ -774,6 +775,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { if (rclick_type == CLICK_NODE) { node_popup->clear(); + node_popup->set_size(Size2(1, 1)); node_popup->add_item(TTR("Rename"), NODE_RENAME); node_popup->add_item(TTR("Remove"), NODE_ERASE); if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) @@ -1288,7 +1290,7 @@ AnimationTreeEditor::AnimationTreeEditor() { p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); p->add_separator(); - p->add_item(TTR("Import Animations.."), MENU_IMPORT_ANIMATIONS); // wtf + p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf p->add_separator(); p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); @@ -1397,7 +1399,7 @@ AnimationTreeEditor::AnimationTreeEditor() { filter_button->set_margin(MARGIN_RIGHT, -10); edit_dialog->add_child(filter_button); filter_button->hide(); - filter_button->set_text(TTR("Filters..")); + filter_button->set_text(TTR("Filters...")); filter_button->connect("pressed", this, "_edit_filters"); set_clip_contents(true); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 915132c75c..05833704d1 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -30,11 +30,10 @@ #include "asset_library_editor_plugin.h" +#include "core/io/json.h" +#include "core/version.h" #include "editor_node.h" #include "editor_settings.h" -#include "io/json.h" - -#include "version_generated.gen.h" void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, int p_rating, const String &p_cost) { @@ -308,7 +307,7 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() { preview_hb->set_v_size_flags(SIZE_EXPAND_FILL); previews->add_child(preview_hb); - get_ok()->set_text(TTR("Install")); + get_ok()->set_text(TTR("Download")); get_cancel()->set_text(TTR("Close")); } /////////////////////////////////////////////////////////////////////////////////// @@ -316,7 +315,6 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() { void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data) { String error_text; - print_line("COMPLETED: " + itos(p_status) + " code: " + itos(p_code) + " data size: " + itos(p_data.size())); switch (p_status) { @@ -371,7 +369,6 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int progress->set_max(download->get_body_size()); progress->set_value(download->get_downloaded_bytes()); - print_line("max: " + itos(download->get_body_size()) + " bytes: " + itos(download->get_downloaded_bytes())); install->set_disabled(false); progress->set_value(download->get_downloaded_bytes()); @@ -410,13 +407,13 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { switch (cstatus) { case HTTPClient::STATUS_RESOLVING: { - status->set_text(TTR("Resolving..")); + status->set_text(TTR("Resolving...")); } break; case HTTPClient::STATUS_CONNECTING: { - status->set_text(TTR("Connecting..")); + status->set_text(TTR("Connecting...")); } break; case HTTPClient::STATUS_REQUESTING: { - status->set_text(TTR("Requesting..")); + status->set_text(TTR("Requesting...")); } break; default: {} } @@ -517,6 +514,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { download = memnew(HTTPRequest); add_child(download); download->connect("request_completed", this, "_http_download_completed"); + download->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); download_error = memnew(AcceptDialog); add_child(download_error); @@ -536,11 +534,9 @@ void EditorAssetLibrary::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(get_icon("Error", "EditorIcons")); + error_tr->set_texture(get_icon("Error", "EditorIcons")); reverse->set_icon(get_icon("Sort", "EditorIcons")); - error_hb->add_child(tf); error_label->raise(); } break; @@ -588,6 +584,8 @@ void EditorAssetLibrary::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree")); + error_tr->set_texture(get_icon("Error", "EditorIcons")); + reverse->set_icon(get_icon("Sort", "EditorIcons")); } break; } } @@ -641,7 +639,7 @@ const char *EditorAssetLibrary::support_key[SUPPORT_MAX] = { void EditorAssetLibrary::_select_author(int p_id) { - //opemn author window + // Open author window } void EditorAssetLibrary::_select_category(int p_id) { @@ -661,16 +659,6 @@ void EditorAssetLibrary::_select_category(int p_id) { void EditorAssetLibrary::_select_asset(int p_id) { _api_request("asset/" + itos(p_id), REQUESTING_ASSET); - - /* - if (description) { - memdelete(description); - } - - - description = memnew( EditorAssetLibraryItemDescription ); - add_child(description); - description->popup_centered_minsize();*/ } void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByteArray &p_data, int p_queue_id) { @@ -747,8 +735,6 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons if (p_status == HTTPRequest::RESULT_SUCCESS) { - print_line("GOT IMAGE YAY!"); - if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) { for (int i = 0; i < headers.size(); i++) { if (headers[i].findn("ETag:") == 0) { // Save etag @@ -778,7 +764,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 PNG file for asset id " + itos(image_queue[p_queue_id].asset_id)); + WARN_PRINTS("Error getting PNG 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("ErrorSign", "EditorIcons")); @@ -811,7 +797,6 @@ void EditorAssetLibrary::_update_image_queue() { } } - print_line("REQUEST ICON FOR: " + itos(E->get().asset_id)); Error err = E->get().request->request(E->get().image_url, headers); if (err != OK) { to_delete.push_back(E->key()); @@ -838,6 +823,7 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag iq.image_index = p_image_index; iq.image_type = p_type; iq.request = memnew(HTTPRequest); + iq.request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); iq.target = p_for; iq.queue_id = ++last_queue_id; @@ -855,7 +841,6 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag void EditorAssetLibrary::_repository_changed(int p_repository_id) { host = repository->get_item_metadata(p_repository_id); - print_line(".." + host); if (templates_only) { _api_request("configure", REQUESTING_CONFIG, "?type=project"); } else { @@ -883,7 +868,8 @@ void EditorAssetLibrary::_search(int p_page) { } args += String() + "sort=" + sort_key[sort->get_selected()]; - args += "&godot_version=" + itos(VERSION_MAJOR) + "." + itos(VERSION_MINOR); + // We use the "branch" version, i.e. major.minor, as patch releases should be compatible + args += "&godot_version=" + String(VERSION_BRANCH); String support_list; for (int i = 0; i < SUPPORT_MAX; i++) { @@ -1066,8 +1052,6 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const return; } - print_line("response: " + itos(p_status) + " code: " + itos(p_code)); - Dictionary d; { Variant js; @@ -1077,8 +1061,6 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const d = js; } - print_line(Variant(d).get_construct_string()); - RequestType requested = requesting; requesting = REQUESTING_NONE; @@ -1390,7 +1372,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { support = memnew(MenuButton); search_hb2->add_child(support); - support->set_text(TTR("Support..")); + support->set_text(TTR("Support...")); support->get_popup()->add_check_item(TTR("Official"), SUPPORT_OFFICIAL); support->get_popup()->add_check_item(TTR("Community"), SUPPORT_COMMUNITY); support->get_popup()->add_check_item(TTR("Testing"), SUPPORT_TESTING); @@ -1462,6 +1444,8 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { error_label = memnew(Label); error_label->add_color_override("color", get_color("error_color", "Editor")); error_hb->add_child(error_label); + error_tr = memnew(TextureRect); + error_hb->add_child(error_tr); description = NULL; diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index b344716c1d..69a65dd3dc 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -194,6 +194,7 @@ class EditorAssetLibrary : public PanelContainer { Button *search; ProgressBar *load_status; HBoxContainer *error_hb; + TextureRect *error_tr; Label *error_label; MenuButton *support; @@ -240,7 +241,6 @@ class EditorAssetLibrary : public PanelContainer { bool active; int queue_id; - int asset_id; ImageType image_type; int image_index; String image_url; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 670f13b6c8..93aeca6632 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -44,6 +44,7 @@ #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/gui/grid_container.h" #include "scene/gui/nine_patch_rect.h" @@ -169,64 +170,10 @@ public: } }; -void CanvasItemEditor::_edit_set_pivot(const Vector2 &mouse_pos) { - List<Node *> &selection = editor_selection->get_selected_node_list(); - - undo_redo->create_action(TTR("Move Pivot")); - - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - - Node2D *n2d = Object::cast_to<Node2D>(E->get()); - if (n2d && n2d->_edit_use_pivot()) { - - Vector2 offset = n2d->_edit_get_pivot(); - Vector2 gpos = n2d->get_global_position(); - - Vector2 local_mouse_pos = n2d->get_canvas_transform().affine_inverse().xform(mouse_pos); - - Vector2 motion_ofs = gpos - local_mouse_pos; - - undo_redo->add_do_method(n2d, "set_global_position", local_mouse_pos); - undo_redo->add_do_method(n2d, "_edit_set_pivot", offset + n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs)); - undo_redo->add_undo_method(n2d, "set_global_position", gpos); - undo_redo->add_undo_method(n2d, "_edit_set_pivot", offset); - for (int i = 0; i < n2d->get_child_count(); i++) { - Node2D *n2dc = Object::cast_to<Node2D>(n2d->get_child(i)); - if (!n2dc) - continue; - - undo_redo->add_do_method(n2dc, "set_global_position", n2dc->get_global_position()); - undo_redo->add_undo_method(n2dc, "set_global_position", n2dc->get_global_position()); - } - } - - Control *cnt = Object::cast_to<Control>(E->get()); - if (cnt) { - - Vector2 old_pivot = cnt->get_pivot_offset(); - Vector2 new_pivot = cnt->get_global_transform_with_canvas().affine_inverse().xform(mouse_pos); - Vector2 old_pos = cnt->get_position(); - - Vector2 top_pos = cnt->get_transform().get_origin(); //remember where top pos was - cnt->set_pivot_offset(new_pivot); - Vector2 new_top_pos = cnt->get_transform().get_origin(); //check where it is now - - Vector2 new_pos = old_pos - (new_top_pos - top_pos); //offset it back - - undo_redo->add_do_method(cnt, "set_pivot_offset", new_pivot); - undo_redo->add_do_method(cnt, "set_position", new_pos); - undo_redo->add_undo_method(cnt, "set_pivot_offset", old_pivot); - undo_redo->add_undo_method(cnt, "set_position", old_pos); - } - } - - undo_redo->commit_action(); -} - void CanvasItemEditor::_snap_if_closer_float(float p_value, float p_target_snap, float &r_current_snap, bool &r_snapped, float p_radius) { float radius = p_radius / zoom; float dist = Math::abs(p_value - p_target_snap); - if (p_radius < 0 || dist < radius && (!r_snapped || dist < Math::abs(r_current_snap - p_value))) { + if ((p_radius < 0 || dist < radius) && (!r_snapped || dist < Math::abs(r_current_snap - p_value))) { r_current_snap = p_target_snap; r_snapped = true; } @@ -250,11 +197,15 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap, Transform2D ci_transform = canvas_item->get_global_transform_with_canvas(); Transform2D to_snap_transform = p_to_snap ? p_to_snap->get_global_transform_with_canvas() : Transform2D(); if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) { - Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position()); - Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size()); - - _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation()); - _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation()); + if (canvas_item->_edit_use_rect()) { + Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position()); + Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size()); + _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation()); + _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation()); + } else { + Point2 position = ci_transform.xform(Point2()); + _snap_if_closer_point(p_value, position, r_current_snap, r_snapped, ci_transform.get_rotation()); + } } } for (int i = 0; i < p_current->get_child_count(); i++) { @@ -263,63 +214,76 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap, } Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const CanvasItem *p_canvas_item, unsigned int p_forced_modes) { - Point2 dist[2]; bool snapped[2] = { false, false }; + bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); // Smart snap using the canvas position Vector2 output = p_target; real_t rotation = 0.0; if (p_canvas_item) { - Point2 begin; - Point2 end; rotation = p_canvas_item->get_global_transform_with_canvas().get_rotation(); - if ((snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { - // Parent sides and center - bool can_snap = false; + // Parent sides and center + if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { - begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0))); - end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1))); - can_snap = true; + Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0))); + Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1))); + _snap_if_closer_point(p_target, begin, output, snapped, rotation); + _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation); + _snap_if_closer_point(p_target, end, output, snapped, rotation); } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) { - begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position()); - end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size()); - can_snap = true; + if (parent_ci->_edit_use_rect()) { + Point2 begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position()); + Point2 end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size()); + _snap_if_closer_point(p_target, begin, output, snapped, rotation); + _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation); + _snap_if_closer_point(p_target, end, output, snapped, rotation); + } else { + Point2 position = p_canvas_item->get_transform().affine_inverse().xform(Point2()); + _snap_if_closer_point(p_target, position, output, snapped, rotation); + } } + } - if (can_snap) { + // Self anchors + if ((is_snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) { + if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { + Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP)))); + Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM)))); _snap_if_closer_point(p_target, begin, output, snapped, rotation); - _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation); _snap_if_closer_point(p_target, end, output, snapped, rotation); } } - // Self anchors (for sides) - if ((snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) { - if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { - begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP)))); - end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM)))); + // Self sides + if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) { + if (p_canvas_item->_edit_use_rect()) { + Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position()); + Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size()); _snap_if_closer_point(p_target, begin, output, snapped, rotation); _snap_if_closer_point(p_target, end, output, snapped, rotation); } } - // Self sides (for anchors) - if ((snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) { - begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position()); - end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size()); - _snap_if_closer_point(p_target, begin, output, snapped, rotation); - _snap_if_closer_point(p_target, end, output, snapped, rotation); + // Self center + if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) { + if (p_canvas_item->_edit_use_rect()) { + Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0); + _snap_if_closer_point(p_target, center, output, snapped, rotation); + } else { + Point2 position = p_canvas_item->get_global_transform_with_canvas().xform(Point2()); + _snap_if_closer_point(p_target, position, output, snapped, rotation); + } } } // Other nodes sides - if ((snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) { + if ((is_snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) { _snap_other_nodes(p_target, output, snapped, get_tree()->get_edited_scene_root(), p_canvas_item); } - if (((snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) { + if (((is_snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) { // Guides if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); @@ -336,15 +300,15 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } } - if (((snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) { + if (((is_snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) { // Grid Point2 offset = grid_offset; if (snap_relative) { - List<Node *> selection = editor_selection->get_selected_node_list(); + List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1 && Object::cast_to<Node2D>(selection[0])) { offset = Object::cast_to<Node2D>(selection[0])->get_global_position(); - } else { - offset = _find_topleftmost_point(); + } else if (selection.size() > 0) { + offset = _get_encompassing_rect_from_list(selection).position; } } Point2 grid_output; @@ -362,8 +326,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } float CanvasItemEditor::snap_angle(float p_target, float p_start) const { - float offset = snap_relative ? p_start : p_target; - return (snap_rotation && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; + return (((snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; } void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { @@ -377,48 +340,20 @@ void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { return; if (k->is_pressed() && !k->is_echo()) { - if (drag_pivot_shortcut.is_valid() && drag_pivot_shortcut->is_shortcut(p_ev) && drag == DRAG_NONE && can_move_pivot) { - //move drag pivot - drag = DRAG_PIVOT; - } else if (set_pivot_shortcut.is_valid() && set_pivot_shortcut->is_shortcut(p_ev) && drag == DRAG_NONE && can_move_pivot) { - if (!Input::get_singleton()->is_mouse_button_pressed(0)) { - List<Node *> selection = editor_selection->get_selected_node_list(); - Vector2 mouse_pos = viewport->get_local_mouse_position(); - if (selection.size() && viewport->get_rect().has_point(mouse_pos)) { - //just in case, make it work if over viewport - mouse_pos = transform.affine_inverse().xform(mouse_pos); - mouse_pos = snap_point(mouse_pos, SNAP_DEFAULT, _get_single_item()); - - _edit_set_pivot(mouse_pos); - } - } - } else if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) { + if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) { // Multiply the grid size grid_step_multiplier = MIN(grid_step_multiplier + 1, 12); - viewport_base->update(); viewport->update(); } else if ((snap_grid || show_grid) && divide_grid_step_shortcut.is_valid() && divide_grid_step_shortcut->is_shortcut(p_ev)) { // Divide the grid size Point2 new_grid_step = grid_step * Math::pow(2.0, grid_step_multiplier - 1); if (new_grid_step.x >= 1.0 && new_grid_step.y >= 1.0) grid_step_multiplier--; - viewport_base->update(); viewport->update(); } } } -void CanvasItemEditor::_tool_select(int p_index) { - - ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, rotate_button, pivot_button, pan_button }; - for (int i = 0; i < TOOL_MAX; i++) { - tb[i]->set_pressed(i == p_index); - } - - viewport->update(); - tool = (Tool)p_index; -} - Object *CanvasItemEditor::_get_editor_data(Object *p_what) { CanvasItem *ci = Object::cast_to<CanvasItem>(p_what); @@ -428,220 +363,129 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) { return memnew(CanvasItemEditorSelectedItem); } -Dictionary CanvasItemEditor::get_state() const { +void CanvasItemEditor::_keying_changed() { - Dictionary state; - state["zoom"] = zoom; - state["ofs"] = Point2(h_scroll->get_value(), v_scroll->get_value()); - //state["ofs"]=-transform.get_origin(); - state["grid_offset"] = grid_offset; - state["grid_step"] = grid_step; - state["snap_rotation_offset"] = snap_rotation_offset; - state["snap_rotation_step"] = snap_rotation_step; - state["snap_active"] = snap_active; - state["snap_node_parent"] = snap_node_parent; - state["snap_node_anchors"] = snap_node_anchors; - state["snap_node_sides"] = snap_node_sides; - state["snap_other_nodes"] = snap_other_nodes; - state["snap_grid"] = snap_grid; - state["snap_guides"] = snap_guides; - state["show_grid"] = show_grid; - state["show_rulers"] = show_rulers; - state["show_guides"] = show_guides; - state["show_helpers"] = show_helpers; - state["snap_rotation"] = snap_rotation; - state["snap_relative"] = snap_relative; - state["snap_pixel"] = snap_pixel; - state["skeleton_show_bones"] = skeleton_show_bones; - return state; + if (AnimationPlayerEditor::singleton->get_key_editor()->is_visible_in_tree()) + animation_hb->show(); + else + animation_hb->hide(); } -void CanvasItemEditor::set_state(const Dictionary &p_state) { - - Dictionary state = p_state; - if (state.has("zoom")) { - zoom = p_state["zoom"]; - } - - if (state.has("ofs")) { - _update_scrollbars(); // i wonder how safe is calling this here.. - Point2 ofs = p_state["ofs"]; - h_scroll->set_value(ofs.x); - v_scroll->set_value(ofs.y); - } - if (state.has("grid_offset")) { - grid_offset = state["grid_offset"]; - } - - if (state.has("grid_step")) { - grid_step = state["grid_step"]; - } - - if (state.has("snap_rotation_step")) { - snap_rotation_step = state["snap_rotation_step"]; - } - - if (state.has("snap_rotation_offset")) { - snap_rotation_offset = state["snap_rotation_offset"]; - } - - if (state.has("snap_active")) { - snap_active = state["snap_active"]; - snap_button->set_pressed(snap_active); - } - - if (state.has("snap_node_parent")) { - snap_node_parent = state["snap_node_parent"]; - int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); - smartsnap_config_popup->set_item_checked(idx, snap_node_parent); - } - - if (state.has("snap_node_anchors")) { - snap_node_anchors = state["snap_node_anchors"]; - int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_ANCHORS); - smartsnap_config_popup->set_item_checked(idx, snap_node_anchors); - } - - if (state.has("snap_node_sides")) { - snap_node_sides = state["snap_node_sides"]; - int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES); - smartsnap_config_popup->set_item_checked(idx, snap_node_sides); - } - - if (state.has("snap_other_nodes")) { - snap_other_nodes = state["snap_other_nodes"]; - int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES); - smartsnap_config_popup->set_item_checked(idx, snap_other_nodes); - } +Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_list) { + ERR_FAIL_COND_V(p_list.empty(), Rect2()); - if (state.has("snap_guides")) { - snap_guides = state["snap_guides"]; - int idx = smartsnap_config_popup->get_item_index(SNAP_USE_GUIDES); - smartsnap_config_popup->set_item_checked(idx, snap_guides); + // Handles the first element + CanvasItem *canvas_item = p_list.front()->get(); + Rect2 rect; + if (canvas_item->_edit_use_rect()) { + rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2()); + } else { + rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(Point2()), Size2()); } - if (state.has("snap_grid")) { - snap_grid = state["snap_grid"]; - int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID); - snap_config_menu->get_popup()->set_item_checked(idx, snap_grid); - } + // 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(); + if (canvas_item->_edit_use_rect()) { + Rect2 current_rect = canvas_item->_edit_get_rect(); - if (state.has("show_grid")) { - show_grid = state["show_grid"]; - int idx = view_menu->get_popup()->get_item_index(SHOW_GRID); - view_menu->get_popup()->set_item_checked(idx, show_grid); + 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)); + rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y))); + } else { + rect.expand_to(xform.xform(Point2())); + } } - if (state.has("show_rulers")) { - 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); - } + return rect; +} - if (state.has("show_guides")) { - show_guides = state["show_guides"]; - int idx = view_menu->get_popup()->get_item_index(SHOW_GUIDES); - view_menu->get_popup()->set_item_checked(idx, show_guides); - } +void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { + if (!p_node) + return; + if (Object::cast_to<Viewport>(p_node)) + return; - if (state.has("show_helpers")) { - show_helpers = state["show_helpers"]; - int idx = view_menu->get_popup()->get_item_index(SHOW_HELPERS); - view_menu->get_popup()->set_item_checked(idx, show_helpers); - } + const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); - if (state.has("snap_rotation")) { - snap_rotation = state["snap_rotation"]; - int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); - snap_config_menu->get_popup()->set_item_checked(idx, snap_rotation); - } + /*bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != ""; + bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); + bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); - if (state.has("snap_relative")) { - snap_relative = state["snap_relative"]; - int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE); - snap_config_menu->get_popup()->set_item_checked(idx, snap_relative); - } + if (!lock_children && editable) {}*/ - if (state.has("snap_pixel")) { - snap_pixel = state["snap_pixel"]; - int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); - snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel); + for (int i = p_node->get_child_count() - 1; i >= 0; i--) { + if (canvas_item && !canvas_item->is_set_as_toplevel()) { + _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { + const CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node); + _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform); + } } - if (state.has("skeleton_show_bones")) { - skeleton_show_bones = state["skeleton_show_bones"]; - int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES); - skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); + if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) { + Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); + if (canvas_item->_edit_use_rect()) { + Rect2 rect = canvas_item->_edit_get_rect(); + if (r_first) { + r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2()); + r_first = false; + } + if (r_rect.size != Size2()) { + r_rect.expand_to(xform.xform(rect.position)); + r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); + r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); + r_rect.expand_to(xform.xform(rect.position + rect.size)); + } + } else { + if (r_first) { + r_rect = Rect2(xform.xform(Point2()), Size2()); + r_first = false; + } else { + r_rect.expand_to(xform.xform(Point2())); + } + } } - - viewport->update(); -} - -void CanvasItemEditor::_add_canvas_item(CanvasItem *p_canvas_item) { - - editor_selection->add_node(p_canvas_item); -} - -void CanvasItemEditor::_remove_canvas_item(CanvasItem *p_canvas_item) { - - editor_selection->remove_node(p_canvas_item); -} -void CanvasItemEditor::_clear_canvas_items() { - - editor_selection->clear(); } -void CanvasItemEditor::_keying_changed() { - - if (AnimationPlayerEditor::singleton->get_key_editor()->is_visible_in_tree()) - animation_hb->show(); - else - animation_hb->hide(); -} - -bool CanvasItemEditor::_is_part_of_subscene(CanvasItem *p_item) { - - Node *scene_node = get_tree()->get_edited_scene_root(); - Node *item_owner = p_item->get_owner(); +Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) { + Rect2 rect; + bool first = true; + _expand_encompassing_rect_using_children(rect, p_node, first); - return item_owner && item_owner != scene_node && p_item != scene_node && item_owner->get_filename() != ""; + return rect; } -void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, Vector<_SelectResult> &r_items, int limit) { +void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) return; if (Object::cast_to<Viewport>(p_node)) return; const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); - CanvasItem *c = Object::cast_to<CanvasItem>(p_node); + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - - if (c && !c->is_set_as_toplevel()) - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), p_parent_xform * c->get_transform(), p_canvas_xform, r_items); - else { + if (canvas_item && !canvas_item->is_set_as_toplevel()) { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), transform, cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); } - - if (limit != 0 && r_items.size() >= limit) + if (p_limit != 0 && r_items.size() >= p_limit) return; } - if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) { - - Rect2 rect = c->_edit_get_rect(); - Transform2D to_local = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse(); - Point2 local_pos = to_local.xform(p_pos); - const real_t local_grab_distance = (to_local.xform(p_pos + Vector2(grab_distance, 0)) - local_pos).length(); - Rect2 local_pos_rect = Rect2(local_pos, Vector2(0, 0)).grow(local_grab_distance); - - if (rect.intersects(local_pos_rect) && c->_edit_is_selected_on_click(local_pos, local_grab_distance)) { - Node2D *node = Object::cast_to<Node2D>(c); + if (canvas_item && canvas_item->is_visible_in_tree()) { + Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse(); + const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length(); + if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) { + Node2D *node = Object::cast_to<Node2D>(canvas_item); _SelectResult res; - res.item = c; + res.item = canvas_item; res.z_index = node ? node->get_z_index() : 0; res.has_z = node; r_items.push_back(res); @@ -651,64 +495,86 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no return; } -void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, List<CanvasItem *> *r_items) { +void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit) { + + Node *scene = editor->get_edited_scene(); + + _find_canvas_items_at_pos(p_pos, scene, r_items, p_limit); + + //Remove invalid results + for (int i = 0; i < r_items.size(); i++) { + Node *node = r_items[i].item; + + // Make sure the selected node is in the current scene, or editable + while (node && node != get_tree()->get_edited_scene_root() && node->get_owner() != scene && !scene->is_editable_instance(node->get_owner())) { + node = node->get_parent(); + }; + + // Replace the node by the group if grouped + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(node); + while (node && node != scene->get_parent()) { + CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node); + if (canvas_item_tmp && node->has_meta("_edit_group_")) { + canvas_item = canvas_item_tmp; + } + node = node->get_parent(); + } + //Remove the item if invalid + if (!canvas_item || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) { + r_items.remove(i); + i--; + } else { + r_items[i].item = canvas_item; + } + } +} + +void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) return; if (Object::cast_to<Viewport>(p_node)) return; - CanvasItem *c = Object::cast_to<CanvasItem>(p_node); + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); + Node *scene = editor->get_edited_scene(); - bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != ""; - bool editable = false; - if (inherited) { - editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); - } + bool editable = p_node == scene || p_node->get_owner() == scene || scene->is_editable_instance(p_node->get_owner()); bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); - if (!lock_children && (!inherited || editable)) { - for (int i = p_node->get_child_count() - 1; i >= 0; i--) { + bool locked = p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); - if (c && !c->is_set_as_toplevel()) - _find_canvas_items_at_rect(p_rect, p_node->get_child(i), p_parent_xform * c->get_transform(), p_canvas_xform, r_items); - else { - CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); - _find_canvas_items_at_rect(p_rect, p_node->get_child(i), transform, cl ? cl->get_transform() : p_canvas_xform, r_items); + if (!lock_children || !editable) { + for (int i = p_node->get_child_count() - 1; i >= 0; i--) { + if (canvas_item && !canvas_item->is_set_as_toplevel()) { + _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { + CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node); + _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform); } } } - if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) { - - Rect2 rect = c->_edit_get_rect(); - Transform2D xform = p_parent_xform * p_canvas_xform * c->get_transform(); + if (canvas_item && canvas_item->is_visible_in_tree() && !locked && editable) { + Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); - if (p_rect.has_point(xform.xform(rect.position)) && - p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) && - p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) && - p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) { + if (canvas_item->_edit_use_rect()) { + Rect2 rect = canvas_item->_edit_get_rect(); + if (p_rect.has_point(xform.xform(rect.position)) && + p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) && + p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) && + p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) { - r_items->push_back(c); + r_items->push_back(canvas_item); + } + } else { + if (p_rect.has_point(xform.xform(Point2()))) { + r_items->push_back(canvas_item); + } } } } -void CanvasItemEditor::_select_click_on_empty_area(Point2 p_click_pos, bool p_append, bool p_box_selection) { - if (!p_append) { - editor_selection->clear(); - viewport->update(); - viewport_base->update(); - }; - - if (p_box_selection) { - // Start a box selection - drag_from = transform.affine_inverse().xform(p_click_pos); - box_selecting = true; - box_selecting_to = drag_from; - } -} - -bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) { +bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append) { bool still_selected = true; if (p_append) { if (editor_selection->is_selected(item)) { @@ -717,7 +583,7 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po still_selected = false; } else { // Add the item to the selection - _append_canvas_item(item); + editor_selection->add_node(item); } } else { if (!editor_selection->is_selected(item)) { @@ -730,216 +596,33 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po } } } - - if (still_selected && p_drag) { - // Drag the node(s) if requested - _prepare_drag(p_click_pos); - } - viewport->update(); - viewport_base->update(); return still_selected; } -void CanvasItemEditor::_key_move(const Vector2 &p_dir, bool p_snap, KeyMoveMODE p_move_mode) { - - if (drag != DRAG_NONE) - return; - - if (editor_selection->get_selected_node_list().empty()) - return; - - undo_redo->create_action(TTR("Move Action"), UndoRedo::MERGE_ENDS); - - 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()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - if (canvas_item->has_meta("_edit_lock_")) - continue; - - Vector2 drag = p_dir; - if (p_snap) - drag *= grid_step * Math::pow(2.0, grid_step_multiplier); - - undo_redo->add_undo_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state()); - - if (p_move_mode == MOVE_VIEW_BASE) { - - // drag = transform.affine_inverse().basis_xform(p_dir); // zoom sensitive - drag = canvas_item->get_global_transform_with_canvas().affine_inverse().basis_xform(drag); - Rect2 local_rect = canvas_item->_edit_get_rect(); - local_rect.position += drag; - undo_redo->add_do_method(canvas_item, "_edit_set_rect", local_rect); - - } else { // p_move_mode==MOVE_LOCAL_BASE || p_move_mode==MOVE_LOCAL_WITH_ROT - - Node2D *node_2d = Object::cast_to<Node2D>(canvas_item); - if (node_2d) { - - if (p_move_mode == MOVE_LOCAL_WITH_ROT) { - Transform2D m; - m.rotate(node_2d->get_rotation()); - drag = m.xform(drag); - } - node_2d->set_position(node_2d->get_position() + drag); - - } else { - Control *control = Object::cast_to<Control>(canvas_item); - if (control) - control->set_position(control->get_position() + drag); +List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_locked, bool remove_canvas_item_if_parent_in_selection) { + List<CanvasItem *> selection; + for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) { + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); + if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (!retreive_locked || !canvas_item->has_meta("_edit_lock_"))) { + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (se) { + selection.push_back(canvas_item); } } } - undo_redo->commit_action(); -} - -Point2 CanvasItemEditor::_find_topleftmost_point() { - - Vector2 tl = Point2(1e10, 1e10); - Rect2 r2; - r2.position = tl; - - 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()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - Rect2 rect = canvas_item->_edit_get_rect(); - Transform2D xform = canvas_item->get_global_transform_with_canvas(); - - r2.expand_to(xform.xform(rect.position)); - r2.expand_to(xform.xform(rect.position + Vector2(rect.size.x, 0))); - r2.expand_to(xform.xform(rect.position + rect.size)); - r2.expand_to(xform.xform(rect.position + Vector2(0, rect.size.y))); - } - - return r2.position; -} - -int CanvasItemEditor::get_item_count() { - - List<Node *> selection = editor_selection->get_selected_node_list(); - - int ic = 0; - 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; - - ic++; - }; - - return ic; -} - -CanvasItem *CanvasItemEditor::_get_single_item() { - - Map<Node *, Object *> &selection = editor_selection->get_selection(); - - CanvasItem *single_item = NULL; - - 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 (single_item) - return NULL; //morethan one - - single_item = canvas_item; - }; - - return single_item; -} - -CanvasItemEditor::DragType CanvasItemEditor::_get_resize_handle_drag_type(const Point2 &p_click, Vector2 &r_point) { - // Returns a drag type if a resize handle is clicked - CanvasItem *canvas_item = _get_single_item(); - - ERR_FAIL_COND_V(!canvas_item, DRAG_NONE); - - Rect2 rect = canvas_item->_edit_get_rect(); - Transform2D xforml = canvas_item->get_global_transform_with_canvas(); - Transform2D xform = transform * xforml; - - Vector2 endpoints[4] = { - - xform.xform(rect.position), - xform.xform(rect.position + Vector2(rect.size.x, 0)), - xform.xform(rect.position + rect.size), - xform.xform(rect.position + Vector2(0, rect.size.y)) - }; - - Vector2 endpointsl[4] = { - - xforml.xform(rect.position), - xforml.xform(rect.position + Vector2(rect.size.x, 0)), - xforml.xform(rect.position + rect.size), - xforml.xform(rect.position + Vector2(0, rect.size.y)) - }; - - DragType dragger[] = { - DRAG_TOP_LEFT, - DRAG_TOP, - DRAG_TOP_RIGHT, - DRAG_RIGHT, - DRAG_BOTTOM_RIGHT, - DRAG_BOTTOM, - DRAG_BOTTOM_LEFT, - DRAG_LEFT - }; - - float radius = (select_handle->get_size().width / 2) * 1.5; - - for (int i = 0; i < 4; i++) { - - int prev = (i + 3) % 4; - int next = (i + 1) % 4; - - r_point = endpointsl[i]; - - Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); - ofs *= 1.4144 * (select_handle->get_size().width / 2); - - ofs += endpoints[i]; - - if (ofs.distance_to(p_click) < radius) - return dragger[i * 2]; - - ofs = (endpoints[i] + endpoints[next]) / 2; - ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2); - - r_point = (endpointsl[i] + endpointsl[next]) / 2; - - if (ofs.distance_to(p_click) < radius) - return dragger[i * 2 + 1]; + if (remove_canvas_item_if_parent_in_selection) { + List<CanvasItem *> filtered_selection; + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + if (!selection.find(E->get()->get_parent())) { + filtered_selection.push_back(E->get()); + } + } + return filtered_selection; + } else { + return selection; } - - return DRAG_NONE; } Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2 anchor) { @@ -958,148 +641,93 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 return p_control->get_transform().xform(position) / parent_size; } -CanvasItemEditor::DragType CanvasItemEditor::_get_anchor_handle_drag_type(const Point2 &p_click, Vector2 &r_point) { - // Returns a drag type if an anchor handle is clicked - CanvasItem *canvas_item = _get_single_item(); - ERR_FAIL_COND_V(!canvas_item, DRAG_NONE); - - Control *control = Object::cast_to<Control>(canvas_item); - ERR_FAIL_COND_V(!control, DRAG_NONE); - - Vector2 anchor_pos[4]; - anchor_pos[0] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_TOP)); - anchor_pos[1] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_TOP)); - anchor_pos[2] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_BOTTOM)); - anchor_pos[3] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_BOTTOM)); - - Rect2 anchor_rects[4]; - for (int i = 0; i < 4; i++) { - anchor_pos[i] = (transform * control->get_global_transform_with_canvas()).xform(_anchor_to_position(control, anchor_pos[i])); - anchor_rects[i] = Rect2(anchor_pos[i], anchor_handle->get_size()); - anchor_rects[i].position -= anchor_handle->get_size() * Vector2(i == 0 || i == 3, i <= 1); - } - - DragType dragger[] = { - DRAG_ANCHOR_TOP_LEFT, - DRAG_ANCHOR_TOP_RIGHT, - DRAG_ANCHOR_BOTTOM_RIGHT, - DRAG_ANCHOR_BOTTOM_LEFT, - }; - - for (int i = 0; i < 4; i++) { - if (anchor_rects[i].has_point(p_click)) { - r_point = transform.affine_inverse().xform(anchor_pos[i]); - if ((anchor_pos[0] == anchor_pos[2]) && (anchor_pos[0].distance_to(p_click) < anchor_handle->get_size().length() / 3.0)) { - return DRAG_ANCHOR_ALL; +void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) { + for (List<CanvasItem *>::Element *E = p_canvas_items.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + if (se) { + se->undo_state = canvas_item->_edit_get_state(); + se->pre_drag_xform = canvas_item->get_global_transform_with_canvas(); + if (canvas_item->_edit_use_rect()) { + se->pre_drag_rect = canvas_item->_edit_get_rect(); } else { - return dragger[i]; + se->pre_drag_rect = Rect2(); + } + se->pre_drag_bones_length = List<float>(); + se->pre_drag_bones_undo_state = List<Dictionary>(); + + // If we have a bone, save the state of all nodes in the IK chain + Node2D *bone = Object::cast_to<Node2D>(canvas_item); + if (bone && bone->has_meta("_edit_bone_")) { + // Check if we have an IK chain + List<Node2D *> bone_ik_list; + bool ik_found = false; + bone = Object::cast_to<Node2D>(bone->get_parent()); + while (bone) { + bone_ik_list.push_back(bone); + if (bone->has_meta("_edit_ik_")) { + ik_found = true; + break; + } else if (!bone->has_meta("_edit_bone_")) { + break; + } + bone = Object::cast_to<Node2D>(bone->get_parent()); + } + + //Save the bone state and length if we have an IK chain + if (ik_found) { + bone = Object::cast_to<Node2D>(canvas_item); + Transform2D bone_xform = bone->get_global_transform(); + for (List<Node2D *>::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) { + bone_xform = bone_xform * bone->get_transform().affine_inverse(); + Node2D *parent_bone = bone_E->get(); + se->pre_drag_bones_length.push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position())); + se->pre_drag_bones_undo_state.push_back(parent_bone->_edit_get_state()); + bone = parent_bone; + } + } } } } - - return DRAG_NONE; } -void CanvasItemEditor::_prepare_drag(const Point2 &p_click_pos) { - - 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()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - +void CanvasItemEditor::_restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones) { + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - se->undo_state = canvas_item->_edit_get_state(); - if (Object::cast_to<Node2D>(canvas_item)) - se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot(); - if (Object::cast_to<Control>(canvas_item)) - se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset(); - - se->pre_drag_xform = canvas_item->get_global_transform_with_canvas(); - se->pre_drag_rect = canvas_item->_edit_get_rect(); - } - - if (selection.size() == 1 && Object::cast_to<Node2D>(selection[0]) && bone_ik_list.size() == 0) { - drag = DRAG_NODE_2D; - drag_point_from = Object::cast_to<Node2D>(selection[0])->get_global_position(); - } else { - drag = DRAG_ALL; - drag_point_from = _find_topleftmost_point(); - } - drag_from = transform.affine_inverse().xform(p_click_pos); -} - -void CanvasItemEditor::incbeg(float &beg, float &end, float inc, float minsize, bool p_symmetric) { - - if (minsize < 0) { - - beg += inc; - if (p_symmetric) - end -= inc; - } else { - - if (p_symmetric) { - beg += inc; - end -= inc; - if (end - beg < minsize) { - float center = (beg + end) / 2.0; - beg = center - minsize / 2.0; - end = center + minsize / 2.0; + canvas_item->_edit_set_state(se->undo_state); + if (restore_bones) { + for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) { + canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); + canvas_item->_edit_set_state(E->get()); } - - } else { - if (end - (beg + inc) < minsize) - beg = end - minsize; - else - beg += inc; } } } -void CanvasItemEditor::incend(float &beg, float &end, float inc, float minsize, bool p_symmetric) { - - if (minsize < 0) { - - end += inc; - if (p_symmetric) - beg -= inc; - } else { - - if (p_symmetric) { - - end += inc; - beg -= inc; - if (end - beg < minsize) { - float center = (beg + end) / 2.0; - beg = center - minsize / 2.0; - end = center + minsize / 2.0; +void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones) { + undo_redo->create_action(action_name); + for (List<CanvasItem *>::Element *E = p_canvas_items.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + 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()) { + 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()); } - - } else { - if ((end + inc) - beg < minsize) - end = beg + minsize; - else - end += inc; } } -} - -void CanvasItemEditor::_append_canvas_item(CanvasItem *p_item) { - - editor_selection->add_node(p_item); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } void CanvasItemEditor::_snap_changed() { ((SnapDialog *)snap_dialog)->get_fields(grid_offset, grid_step, snap_rotation_offset, snap_rotation_step); grid_step_multiplier = 0; - viewport_base->update(); viewport->update(); } @@ -1111,7 +739,7 @@ void CanvasItemEditor::_selection_result_pressed(int p_result) { CanvasItem *item = selection_results[p_result].item; if (item) - _select_click_on_item(item, Point2(), additive_selection, false); + _select_click_on_item(item, Point2(), selection_menu_additive_selection); } void CanvasItemEditor::_selection_menu_hide() { @@ -1121,121 +749,13 @@ void CanvasItemEditor::_selection_menu_hide() { selection_menu->set_size(Vector2(0, 0)); } -void CanvasItemEditor::_list_select(const Ref<InputEventMouseButton> &b) { - - Point2 click = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position()); - - Node *scene = editor->get_edited_scene(); - if (!scene) - return; - - _find_canvas_items_at_pos(click, scene, transform, Transform2D(), selection_results); - - for (int i = 0; i < selection_results.size(); i++) { - CanvasItem *item = selection_results[i].item; - if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) { - //invalid result - selection_results.remove(i); - i--; - } - } - - if (selection_results.size() == 1) { - - CanvasItem *item = selection_results[0].item; - selection_results.clear(); - - additive_selection = b->get_shift(); - - if (!_select_click_on_item(item, click, additive_selection, false)) - return; - - } else if (!selection_results.empty()) { - - selection_results.sort(); - - NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); - StringName root_name = root_path.get_name(root_path.get_name_count() - 1); - - for (int i = 0; i < selection_results.size(); i++) { - - CanvasItem *item = selection_results[i].item; - - Ref<Texture> icon; - if (item->has_meta("_editor_icon")) - icon = item->get_meta("_editor_icon"); - else - icon = get_icon(has_icon(item->get_class(), "EditorIcons") ? item->get_class() : String("Object"), "EditorIcons"); - - String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path()); - - selection_menu->add_item(item->get_name()); - selection_menu->set_item_icon(i, icon); - selection_menu->set_item_metadata(i, node_path); - selection_menu->set_item_tooltip(i, String(item->get_name()) + "\nType: " + item->get_class() + "\nPath: " + node_path); - } - - additive_selection = b->get_shift(); - - selection_menu->set_global_position(b->get_global_position()); - selection_menu->popup(); - selection_menu->call_deferred("grab_click_focus"); - selection_menu->set_invalidate_click_until_motion(); - - return; - } -} - -void CanvasItemEditor::_update_cursor() { - - CursorShape c = CURSOR_ARROW; - switch (drag) { - case DRAG_NONE: - if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { - c = CURSOR_DRAG; - } else { - switch (tool) { - case TOOL_MOVE: - c = CURSOR_MOVE; - break; - case TOOL_EDIT_PIVOT: - c = CURSOR_CROSS; - break; - case TOOL_PAN: - c = CURSOR_DRAG; - break; - } - } - break; - case DRAG_LEFT: - case DRAG_RIGHT: - c = CURSOR_HSIZE; - break; - case DRAG_TOP: - case DRAG_BOTTOM: - c = CURSOR_VSIZE; - break; - case DRAG_TOP_LEFT: - case DRAG_BOTTOM_RIGHT: - c = CURSOR_FDIAGSIZE; - break; - case DRAG_TOP_RIGHT: - case DRAG_BOTTOM_LEFT: - c = CURSOR_BDIAGSIZE; - break; - case DRAG_ALL: - case DRAG_NODE_2D: - c = CURSOR_MOVE; - break; - } - viewport->set_default_cursor_shape(c); -} - -void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event) { - +bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; - if (b.is_valid()) { - if (b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { + Ref<InputEventMouseMotion> m = p_event; + + // Start dragging a guide + if (drag_type == DRAG_NONE) { + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { if (show_guides && show_rulers && EditorNode::get_singleton()->get_edited_scene()) { Transform2D xform = viewport_scrollable->get_transform() * transform; // Retrieve the guide lists @@ -1251,48 +771,64 @@ void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event) // Press button if (b->get_position().x < RULER_WIDTH && b->get_position().y < RULER_WIDTH) { // Drag a new double guide - drag = DRAG_DOUBLE_GUIDE; - edited_guide_index = -1; + drag_type = DRAG_DOUBLE_GUIDE; + dragged_guide_index = -1; + return true; } else if (b->get_position().x < RULER_WIDTH) { // Check if we drag an existing horizontal guide float minimum = 1e20; - edited_guide_index = -1; + dragged_guide_index = -1; for (int i = 0; i < hguides.size(); i++) { if (ABS(xform.xform(Point2(0, hguides[i])).y - b->get_position().y) < MIN(minimum, 8)) { - edited_guide_index = i; + dragged_guide_index = i; } } - if (edited_guide_index >= 0) { + if (dragged_guide_index >= 0) { // Drag an existing horizontal guide - drag = DRAG_H_GUIDE; + drag_type = DRAG_H_GUIDE; } else { // Drag a new vertical guide - drag = DRAG_V_GUIDE; + drag_type = DRAG_V_GUIDE; } + return true; } else if (b->get_position().y < RULER_WIDTH) { // Check if we drag an existing vertical guide float minimum = 1e20; - edited_guide_index = -1; + dragged_guide_index = -1; for (int i = 0; i < vguides.size(); i++) { if (ABS(xform.xform(Point2(vguides[i], 0)).x - b->get_position().x) < MIN(minimum, 8)) { - edited_guide_index = i; + dragged_guide_index = i; } } - if (edited_guide_index >= 0) { + if (dragged_guide_index >= 0) { // Drag an existing vertical guide - drag = DRAG_V_GUIDE; + drag_type = DRAG_V_GUIDE; } else { // Drag a new vertical guide - drag = DRAG_H_GUIDE; + drag_type = DRAG_H_GUIDE; } + drag_from = xform.affine_inverse().xform(b->get_position()); + return true; } } } + } + + if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_V_GUIDE || drag_type == DRAG_H_GUIDE) { + // Move the guide + if (m.is_valid()) { + Transform2D xform = viewport_scrollable->get_transform() * transform; + drag_to = xform.affine_inverse().xform(m->get_position()); + + dragged_guide_pos = xform.xform(snap_point(drag_to, SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES)); + viewport->update(); + return true; + } - if (b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { - // Release button + // Release confirms the guide move + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { if (show_guides && EditorNode::get_singleton()->get_edited_scene()) { Transform2D xform = viewport_scrollable->get_transform() * transform; @@ -1307,65 +843,65 @@ void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event) } Point2 edited = snap_point(xform.affine_inverse().xform(b->get_position()), SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES); - if (drag == DRAG_V_GUIDE) { + if (drag_type == DRAG_V_GUIDE) { Array prev_vguides = vguides.duplicate(); if (b->get_position().x > RULER_WIDTH) { // Adds a new vertical guide - if (edited_guide_index >= 0) { - vguides[edited_guide_index] = edited.x; + if (dragged_guide_index >= 0) { + vguides[dragged_guide_index] = edited.x; undo_redo->create_action(TTR("Move vertical guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } else { vguides.push_back(edited.x); undo_redo->create_action(TTR("Create new vertical guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } } else { - if (edited_guide_index >= 0) { - vguides.remove(edited_guide_index); + if (dragged_guide_index >= 0) { + vguides.remove(dragged_guide_index); undo_redo->create_action(TTR("Remove vertical guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } } - } else if (drag == DRAG_H_GUIDE) { + } else if (drag_type == DRAG_H_GUIDE) { Array prev_hguides = hguides.duplicate(); if (b->get_position().y > RULER_WIDTH) { // Adds a new horizontal guide - if (edited_guide_index >= 0) { - hguides[edited_guide_index] = edited.y; + if (dragged_guide_index >= 0) { + hguides[dragged_guide_index] = edited.y; undo_redo->create_action(TTR("Move horizontal guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } else { hguides.push_back(edited.y); undo_redo->create_action(TTR("Create new horizontal guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } } else { - if (edited_guide_index >= 0) { - hguides.remove(edited_guide_index); + if (dragged_guide_index >= 0) { + hguides.remove(dragged_guide_index); undo_redo->create_action(TTR("Remove horizontal guide")); undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } } - } else if (drag == DRAG_DOUBLE_GUIDE) { + } else if (drag_type == DRAG_DOUBLE_GUIDE) { Array prev_hguides = hguides.duplicate(); Array prev_vguides = vguides.duplicate(); if (b->get_position().x > RULER_WIDTH && b->get_position().y > RULER_WIDTH) { @@ -1377,880 +913,1089 @@ void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event) undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides); undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides); - undo_redo->add_undo_method(viewport_base, "update"); + undo_redo->add_undo_method(viewport, "update"); undo_redo->commit_action(); } } } - if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_V_GUIDE || drag == DRAG_H_GUIDE) { - drag = DRAG_NONE; - viewport_base->update(); - } + drag_type = DRAG_NONE; + viewport->update(); + return true; } } + return false; +} - Ref<InputEventMouseMotion> m = p_event; - if (m.is_valid()) { - if (!viewport_base->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) { - viewport_base->call_deferred("grab_focus"); +bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + if (b.is_valid()) { + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_DOWN) { + // Scroll or pan down + if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { + view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); + _update_scrollbars(); + viewport->update(); + } else { + _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position()); + } + return true; } - if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_H_GUIDE || drag == DRAG_V_GUIDE) { - Transform2D xform = viewport_scrollable->get_transform() * transform; - Point2 mouse_pos = m->get_position(); - mouse_pos = xform.affine_inverse().xform(mouse_pos); - mouse_pos = xform.xform(snap_point(mouse_pos, SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES)); - edited_guide_pos = mouse_pos; - viewport_base->update(); + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_UP) { + // Scroll or pan up + if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { + view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); + _update_scrollbars(); + viewport->update(); + } else { + _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position()); + } + return true; } - } - Ref<InputEventKey> k = p_event; - if (k.is_valid()) { - if (k->is_pressed() && drag == DRAG_NONE) { - // Move the object with the arrow keys - KeyMoveMODE move_mode = MOVE_VIEW_BASE; - if (k->get_alt()) move_mode = MOVE_LOCAL_BASE; - if (k->get_control() || k->get_metakey()) move_mode = MOVE_LOCAL_WITH_ROT; + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_LEFT) { + // Pan left + if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { + view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); + _update_scrollbars(); + viewport->update(); + return true; + } + } - if (k->get_scancode() == KEY_UP) - _key_move(Vector2(0, -1), k->get_shift(), move_mode); - else if (k->get_scancode() == KEY_DOWN) - _key_move(Vector2(0, 1), k->get_shift(), move_mode); - else if (k->get_scancode() == KEY_LEFT) - _key_move(Vector2(-1, 0), k->get_shift(), move_mode); - else if (k->get_scancode() == KEY_RIGHT) - _key_move(Vector2(1, 0), k->get_shift(), move_mode); - else if (k->get_scancode() == KEY_ESCAPE) { - editor_selection->clear(); + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_RIGHT) { + // Pan right + if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { + view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); + _update_scrollbars(); viewport->update(); - } else - return; + return true; + } + } - accept_event(); + if (drag_type == DRAG_NONE) { + 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; + } } - } -} -void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { + if (drag_type == DRAG_PAN) { + if (!b->is_pressed()) { + // Stop panning the viewport (for any mouse button press) + drag_type = DRAG_NONE; + } + } + } - { - EditorNode *en = editor; - EditorPluginList *over_plugin_list = en->get_editor_plugins_over(); + 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->is_pressed() && !k->is_echo()) { + //Pan the viewport + drag_type = DRAG_PAN; + } + } else if (drag_type == DRAG_PAN) { + if (!k->is_pressed()) { + // Stop panning the viewport (for any mouse button press) + drag_type = DRAG_NONE; + } + } + } + } - if (!over_plugin_list->empty()) { - bool discard = over_plugin_list->forward_gui_input(p_event); - if (discard) { - accept_event(); - return; + Ref<InputEventMouseMotion> m = p_event; + if (m.is_valid()) { + if (drag_type == DRAG_PAN) { + // Pan the viewport + Point2i relative; + if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { + relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect()); + } else { + relative = m->get_relative(); } + view_offset.x -= relative.x / zoom; + view_offset.y -= relative.y / zoom; + _update_scrollbars(); + viewport->update(); + return true; } } Ref<InputEventMagnifyGesture> magnify_gesture = p_event; if (magnify_gesture.is_valid()) { - + // Zoom gesture _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - return; + return true; } Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { - + // Pan gesture const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta(); - h_scroll->set_value(h_scroll->get_value() + delta.x); - v_scroll->set_value(v_scroll->get_value() + delta.y); - return; + view_offset.x += delta.x; + view_offset.y += delta.y; + _update_scrollbars(); + viewport->update(); + return true; } + return false; +} + +bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseMotion> m = p_event; Ref<InputEventMouseButton> b = p_event; - if (b.is_valid()) { - // Button event + Ref<InputEventKey> k = p_event; - if (b->get_button_index() == BUTTON_WHEEL_DOWN) { - // Scroll or pan down - if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { - v_scroll->set_value(v_scroll->get_value() + int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor()); - _update_scroll(0); - viewport->update(); - } else { - _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position()); + // Drag the pivot (in pivot mode / with V key) + if (drag_type == DRAG_NONE) { + if ((b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) || + (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_scancode() == KEY_V)) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + + // Filters the selection with nodes that allow setting the pivot + drag_selection = List<CanvasItem *>(); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + if (canvas_item->_edit_use_pivot()) { + drag_selection.push_back(canvas_item); + } } - return; - } + // Start dragging if we still have nodes + if (drag_selection.size() > 0) { + drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position()); + Vector2 new_pos; + if (drag_selection.size() == 1) + new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, drag_selection[0]); + else + new_pos = snap_point(drag_from, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL); + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + canvas_item->_edit_set_pivot(canvas_item->get_global_transform_with_canvas().affine_inverse().xform(new_pos)); + } - if (b->get_button_index() == BUTTON_WHEEL_UP) { - // Scroll or pan up - if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { - v_scroll->set_value(v_scroll->get_value() - int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor()); - _update_scroll(0); - viewport->update(); - } else { - _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position()); + drag_type = DRAG_PIVOT; + _save_canvas_item_state(drag_selection); } + return true; + } + } - return; + if (drag_type == DRAG_PIVOT) { + // Move the pivot + if (m.is_valid()) { + drag_to = transform.affine_inverse().xform(m->get_position()); + _restore_canvas_item_state(drag_selection); + Vector2 new_pos; + if (drag_selection.size() == 1) + new_pos = snap_point(drag_to, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, drag_selection[0]); + else + new_pos = snap_point(drag_to, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL); + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + canvas_item->_edit_set_pivot(canvas_item->get_global_transform_with_canvas().affine_inverse().xform(new_pos)); + } + return true; } - if (b->get_button_index() == BUTTON_WHEEL_LEFT) { - // Pan left - if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { + // Confirm the pivot move + if ((b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) || + (k.is_valid() && !k->is_pressed() && k->get_scancode() == KEY_V)) { + _commit_canvas_item_state(drag_selection, TTR("Move pivot")); + drag_type = DRAG_NONE; + return true; + } - h_scroll->set_value(h_scroll->get_value() - int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor()); - } + // Cancel a drag + if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + _restore_canvas_item_state(drag_selection); + drag_type = DRAG_NONE; + viewport->update(); + return true; } + } + return false; +} - if (b->get_button_index() == BUTTON_WHEEL_RIGHT) { - // Pan right - if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { +void CanvasItemEditor::_solve_IK(Node2D *leaf_node, Point2 target_position) { + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(leaf_node); + if (se && !se->pre_drag_bones_undo_state.empty()) { + + // Build the node list + Point2 leaf_pos = target_position; + + List<Node2D *> joints_list; + List<Point2> joints_pos; + Node2D *joint = leaf_node; + Transform2D joint_transform = leaf_node->get_global_transform_with_canvas(); + for (int i = 0; i < se->pre_drag_bones_undo_state.size() + 1; i++) { + joints_list.push_back(joint); + joints_pos.push_back(joint_transform.get_origin()); + joint_transform = joint_transform * joint->get_transform().affine_inverse(); + joint = Object::cast_to<Node2D>(joint->get_parent()); + } + Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin(); - h_scroll->set_value(h_scroll->get_value() + int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor()); + // Restraints the node to a maximum distance is necessary + float total_len = 0; + for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { + total_len += E->get(); + } + if ((root_pos.distance_to(leaf_pos)) > total_len) { + Vector2 rel = leaf_pos - root_pos; + rel = rel.normalized() * total_len; + leaf_pos = root_pos + rel; + } + joints_pos[0] = leaf_pos; + + // Run the solver + int solver_iterations = 64; + float solver_k = 0.3; + + // Build the position list + for (int i = 0; i < solver_iterations; i++) { + // Handle the leaf joint + int node_id = 0; + for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { + Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized(); + int len = E->get(); + if (E == se->pre_drag_bones_length.front()) { + joints_pos[1] = joints_pos[1].linear_interpolate(joints_pos[0] + len * direction, solver_k); + } else if (E == se->pre_drag_bones_length.back()) { + joints_pos[node_id] = joints_pos[node_id].linear_interpolate(joints_pos[node_id + 1] - len * direction, solver_k); + } else { + Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0; + joints_pos[node_id] = joints_pos[node_id].linear_interpolate(center - (direction * len) / 2.0, solver_k); + joints_pos[node_id + 1] = joints_pos[node_id + 1].linear_interpolate(center + (direction * len) / 2.0, solver_k); + } + node_id++; } } - if (b->get_button_index() == BUTTON_RIGHT) { - - if (b->is_pressed() && (tool == TOOL_SELECT && b->get_alt())) { - // Open the selection list - _list_select(b); - return; + // Set the position + float total_rot = 0.0f; + for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) { + Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized(); + Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized(); + float rot = current.angle_to(target); + if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) { + rot = -rot; } + joints_list[node_id]->rotate(rot); + total_rot += rot; + } - if (get_item_count() > 0 && drag != DRAG_NONE) { - // Cancel a drag - if (bone_ik_list.size()) { - for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) { - E->get().node->_edit_set_state(E->get().orig_state); - } + joints_list[0]->rotate(-total_rot); + } +} - bone_ik_list.clear(); +bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; + // Start rotation + if (drag_type == DRAG_NONE) { + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { + drag_selection = _get_edited_canvas_items(); + if (drag_selection.size() > 0 && ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) { + drag_type = DRAG_ROTATE; + drag_from = transform.affine_inverse().xform(b->get_position()); + CanvasItem *canvas_item = drag_selection[0]; + if (canvas_item->_edit_use_pivot()) { + drag_rotation_center = canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_pivot()); } else { - 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()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - canvas_item->_edit_set_state(se->undo_state); - if (Object::cast_to<Node2D>(canvas_item)) - Object::cast_to<Node2D>(canvas_item)->_edit_set_pivot(se->undo_pivot); - if (Object::cast_to<Control>(canvas_item)) - Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot); - } + drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin(); } + _save_canvas_item_state(drag_selection); + return true; + } + } + } - drag = DRAG_NONE; - viewport->update(); - can_move_pivot = false; - - } else if (box_selecting) { - // Cancel box selection - box_selecting = false; + if (drag_type == DRAG_ROTATE) { + // Rotate the node + if (m.is_valid()) { + _restore_canvas_item_state(drag_selection); + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + drag_to = transform.affine_inverse().xform(m->get_position()); + canvas_item->_edit_set_rotation(snap_angle(canvas_item->_edit_get_rotation() + (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), canvas_item->_edit_get_rotation())); viewport->update(); } - return; + return true; } - if (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT) { - if (b->is_pressed()) - // Open the selection list - _list_select(b); - return; + // 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")); + drag_type = DRAG_NONE; + return true; } - if (b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) { - if (b->is_pressed()) { - // Set the pivot point - Point2 mouse_pos = b->get_position(); - mouse_pos = transform.affine_inverse().xform(mouse_pos); - mouse_pos = snap_point(mouse_pos, SNAP_DEFAULT, _get_single_item()); - _edit_set_pivot(mouse_pos); - } - return; + // Cancel a drag + if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + _restore_canvas_item_state(drag_selection); + drag_type = DRAG_NONE; + viewport->update(); + return true; } + } + return false; +} - if (tool == TOOL_PAN || b->get_button_index() != BUTTON_LEFT || Input::get_singleton()->is_key_pressed(KEY_SPACE)) - // Pan the view - return; - - // -- From now we consider that the button is BUTTON_LEFT -- - - if (!b->is_pressed()) { - - if (drag != DRAG_NONE) { - // Stop dragging - if (undo_redo) { +bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; - if (bone_ik_list.size()) { - undo_redo->create_action(TTR("Edit IK Chain")); + // Open a sub-scene on double-click + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && b->is_doubleclick() && tool == TOOL_SELECT) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + if (selection.size() == 1) { + CanvasItem *canvas_item = selection[0]; + if (canvas_item->get_filename() != "" && canvas_item != editor->get_edited_scene()) { + editor->open_request(canvas_item->get_filename()); + return true; + } + } + } + return false; +} - for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) { +bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; - undo_redo->add_do_method(E->get().node, "_edit_set_state", E->get().node->_edit_get_state()); - undo_redo->add_undo_method(E->get().node, "_edit_set_state", E->get().orig_state); - } + // 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) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + if (selection.size() == 1) { + Control *control = Object::cast_to<Control>(selection[0]); + if (control && !Object::cast_to<Container>(control->get_parent())) { + Vector2 anchor_pos[4]; + anchor_pos[0] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_TOP)); + anchor_pos[1] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_TOP)); + anchor_pos[2] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_BOTTOM)); + anchor_pos[3] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_BOTTOM)); - undo_redo->add_do_method(viewport, "update"); - undo_redo->add_undo_method(viewport, "update"); + Rect2 anchor_rects[4]; + for (int i = 0; i < 4; i++) { + anchor_pos[i] = (transform * control->get_global_transform_with_canvas()).xform(_anchor_to_position(control, anchor_pos[i])); + anchor_rects[i] = Rect2(anchor_pos[i], anchor_handle->get_size()); + anchor_rects[i].position -= anchor_handle->get_size() * Vector2(i == 0 || i == 3, i <= 1); + } - bone_ik_list.clear(); + DragType dragger[] = { + DRAG_ANCHOR_TOP_LEFT, + DRAG_ANCHOR_TOP_RIGHT, + DRAG_ANCHOR_BOTTOM_RIGHT, + DRAG_ANCHOR_BOTTOM_LEFT, + }; - undo_redo->commit_action(); - } else { - undo_redo->create_action(TTR("Edit CanvasItem")); - - 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()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - Variant state = canvas_item->_edit_get_state(); - undo_redo->add_do_method(canvas_item, "_edit_set_state", state); - undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state); - { - Node2D *pvt = Object::cast_to<Node2D>(canvas_item); - if (pvt && pvt->_edit_use_pivot()) { - undo_redo->add_do_method(canvas_item, "_edit_set_pivot", pvt->_edit_get_pivot()); - undo_redo->add_undo_method(canvas_item, "_edit_set_pivot", se->undo_pivot); - } - - Control *cnt = Object::cast_to<Control>(canvas_item); - if (cnt) { - undo_redo->add_do_method(canvas_item, "set_pivot_offset", cnt->get_pivot_offset()); - undo_redo->add_undo_method(canvas_item, "set_pivot_offset", se->undo_pivot); - } + for (int i = 0; i < 4; i++) { + if (anchor_rects[i].has_point(b->get_position())) { + if ((anchor_pos[0] == anchor_pos[2]) && (anchor_pos[0].distance_to(b->get_position()) < anchor_handle->get_size().length() / 3.0)) { + drag_type = DRAG_ANCHOR_ALL; + } else { + drag_type = dragger[i]; } + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = List<CanvasItem *>(); + drag_selection.push_back(control); + _save_canvas_item_state(drag_selection); + return true; } - undo_redo->commit_action(); } } - - drag = DRAG_NONE; - viewport->update(); - can_move_pivot = false; } + } + } - if (box_selecting) { - // Stop box selection - Node *scene = editor->get_edited_scene(); - if (scene) { + if (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_TOP_RIGHT || drag_type == DRAG_ANCHOR_BOTTOM_RIGHT || drag_type == DRAG_ANCHOR_BOTTOM_LEFT || drag_type == DRAG_ANCHOR_ALL) { + // Drag the anchor + if (m.is_valid()) { + _restore_canvas_item_state(drag_selection); + Control *control = Object::cast_to<Control>(drag_selection[0]); - List<CanvasItem *> selitems; + drag_to = transform.affine_inverse().xform(m->get_position()); - Point2 bsfrom = transform.xform(drag_from); - Point2 bsto = transform.xform(box_selecting_to); - if (bsfrom.x > bsto.x) - SWAP(bsfrom.x, bsto.x); - if (bsfrom.y > bsto.y) - SWAP(bsfrom.y, bsto.y); + Transform2D xform = control->get_global_transform_with_canvas().affine_inverse(); - _find_canvas_items_at_rect(Rect2(bsfrom, bsto - bsfrom), scene, transform, Transform2D(), &selitems); + Point2 previous_anchor; + previous_anchor.x = (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_BOTTOM_LEFT) ? control->get_anchor(MARGIN_LEFT) : control->get_anchor(MARGIN_RIGHT); + previous_anchor.y = (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_TOP_RIGHT) ? control->get_anchor(MARGIN_TOP) : control->get_anchor(MARGIN_BOTTOM); + previous_anchor = xform.affine_inverse().xform(_anchor_to_position(control, previous_anchor)); - for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) { + Vector2 new_anchor = xform.xform(snap_point(previous_anchor + (drag_to - drag_from), SNAP_GRID | SNAP_OTHER_NODES, control, SNAP_NODE_PARENT | SNAP_NODE_SIDES | SNAP_NODE_CENTER)); + new_anchor = _position_to_anchor(control, new_anchor).snapped(Vector2(0.001, 0.001)); - _append_canvas_item(E->get()); - } - } + bool use_single_axis = m->get_shift(); + Vector2 drag_vector = xform.xform(drag_to) - xform.xform(drag_from); + bool use_y = Math::abs(drag_vector.y) > Math::abs(drag_vector.x); - box_selecting = false; - viewport->update(); + switch (drag_type) { + case DRAG_ANCHOR_TOP_LEFT: + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false); + break; + case DRAG_ANCHOR_TOP_RIGHT: + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false); + break; + case DRAG_ANCHOR_BOTTOM_RIGHT: + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); + if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); + break; + case DRAG_ANCHOR_BOTTOM_LEFT: + if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); + 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); + break; + default: + break; } - return; + return true; + } + + // Confirms new anchor position + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { + _commit_canvas_item_state(drag_selection, TTR("Move anchor")); + drag_type = DRAG_NONE; + return true; } - // -- From now we consider that the button is BUTTON_LEFT and that it is pressed -- + // Cancel a drag + if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + _restore_canvas_item_state(drag_selection); + drag_type = DRAG_NONE; + viewport->update(); + return true; + } + } + return false; +} - Map<ObjectID, BoneList>::Element *Cbone = NULL; //closest +bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; - { - bone_ik_list.clear(); - float closest_dist = 1e20; - int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width"); - for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { + // Drag resize handles + if (drag_type == DRAG_NONE) { + 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) { + CanvasItem *canvas_item = selection[0]; + if (canvas_item->_edit_use_rect()) { + Rect2 rect = canvas_item->_edit_get_rect(); + Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); + + Vector2 endpoints[4] = { + xform.xform(rect.position), + xform.xform(rect.position + Vector2(rect.size.x, 0)), + xform.xform(rect.position + rect.size), + xform.xform(rect.position + Vector2(0, rect.size.y)) + }; + + DragType dragger[] = { + DRAG_TOP_LEFT, + DRAG_TOP, + DRAG_TOP_RIGHT, + DRAG_RIGHT, + DRAG_BOTTOM_RIGHT, + DRAG_BOTTOM, + DRAG_BOTTOM_LEFT, + DRAG_LEFT + }; + + DragType resize_drag = DRAG_NONE; + float radius = (select_handle->get_size().width / 2) * 1.5; - if (E->get().from == E->get().to) - continue; - Vector2 s[2] = { - E->get().from, - E->get().to - }; + for (int i = 0; i < 4; i++) { + int prev = (i + 3) % 4; + int next = (i + 1) % 4; + + Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); + ofs *= (select_handle->get_size().width / 2); + ofs += endpoints[i]; + if (ofs.distance_to(b->get_position()) < radius) + resize_drag = dragger[i * 2]; + + ofs = (endpoints[i] + endpoints[next]) / 2; + ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2); + if (ofs.distance_to(b->get_position()) < radius) + resize_drag = dragger[i * 2 + 1]; + } - Vector2 p = Geometry::get_closest_point_to_segment_2d(b->get_position(), s); - float d = p.distance_to(b->get_position()); - if (d < bone_width && d < closest_dist) { - Cbone = E; - closest_dist = d; + if (resize_drag != DRAG_NONE) { + drag_type = resize_drag; + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = List<CanvasItem *>(); + drag_selection.push_back(canvas_item); + _save_canvas_item_state(drag_selection); + return true; + } } } + } + } - if (Cbone) { - Node2D *b = Object::cast_to<Node2D>(ObjectDB::get_instance(Cbone->get().bone)); - - if (b) { - - bool ik_found = false; - - bool first = true; - - while (b) { + if (drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT || drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM || + drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT) { + // Resize the node + if (m.is_valid()) { + CanvasItem *canvas_item = drag_selection[0]; + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + //Reset state + canvas_item->_edit_set_state(se->undo_state); - CanvasItem *pi = b->get_parent_item(); - if (!pi) - break; + bool uniform = m->get_shift(); + bool symmetric = m->get_alt(); - float len = pi->get_global_transform().get_origin().distance_to(b->get_global_position()); - b = Object::cast_to<Node2D>(pi); - if (!b) - break; + Rect2 local_rect = canvas_item->_edit_get_rect(); + float aspect = local_rect.get_size().y / local_rect.get_size().x; + Point2 current_begin = local_rect.get_position(); + Point2 current_end = local_rect.get_position() + local_rect.get_size(); + Point2 max_begin = (symmetric) ? (current_begin + current_end - canvas_item->_edit_get_minimum_size()) / 2.0 : current_end - canvas_item->_edit_get_minimum_size(); + Point2 min_end = (symmetric) ? (current_begin + current_end + canvas_item->_edit_get_minimum_size()) / 2.0 : current_begin + canvas_item->_edit_get_minimum_size(); + Point2 center = (current_begin + current_end) / 2.0; + + drag_to = transform.affine_inverse().xform(m->get_position()); + + Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse(); + + Point2 drag_to_snapped_begin = snap_point(xform.affine_inverse().xform(current_begin) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, canvas_item); + Point2 drag_to_snapped_end = snap_point(xform.affine_inverse().xform(current_end) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, canvas_item); + Point2 drag_begin = xform.xform(drag_to_snapped_begin); + Point2 drag_end = xform.xform(drag_to_snapped_end); + + // Horizontal resize + if (drag_type == DRAG_LEFT || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_BOTTOM_LEFT) { + current_begin.x = MIN(drag_begin.x, max_begin.x); + } else if (drag_type == DRAG_RIGHT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_RIGHT) { + current_end.x = MAX(drag_end.x, min_end.x); + } - if (first) { + // Vertical resize + if (drag_type == DRAG_TOP || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT) { + current_begin.y = MIN(drag_begin.y, max_begin.y); + } else if (drag_type == DRAG_BOTTOM || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT) { + current_end.y = MAX(drag_end.y, min_end.y); + } - bone_orig_xform = b->get_global_transform(); - first = false; + // Uniform resize + if (uniform) { + if (drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT) { + current_end.y = current_begin.y + aspect * (current_end.x - current_begin.x); + } else if (drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM) { + current_end.x = current_begin.x + (current_end.y - current_begin.y) / aspect; + } else { + if (aspect >= 1.0) { + if (drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT) { + current_begin.y = current_end.y - aspect * (current_end.x - current_begin.x); + } else { + current_end.y = current_begin.y + aspect * (current_end.x - current_begin.x); } - - BoneIK bik; - bik.node = b; - bik.len = len; - bik.orig_state = b->_edit_get_state(); - - bone_ik_list.push_back(bik); - - if (b->has_meta("_edit_ik_")) { - - ik_found = bone_ik_list.size() > 1; - break; + } else { + if (drag_type == DRAG_TOP_LEFT || drag_type == DRAG_BOTTOM_LEFT) { + current_begin.x = current_end.x - (current_end.y - current_begin.y) / aspect; + } else { + current_end.x = current_begin.x + (current_end.y - current_begin.y) / aspect; } - - if (!pi->has_meta("_edit_bone_")) - break; } - - if (!ik_found) - bone_ik_list.clear(); } } - } - - // Single selected item - CanvasItem *canvas_item = _get_single_item(); - if (canvas_item) { - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - ERR_FAIL_COND(!se); - - Point2 click = b->get_position(); - - // Rotation - if ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) { - drag = DRAG_ROTATE; - drag_from = transform.affine_inverse().xform(click); - se->undo_state = canvas_item->_edit_get_state(); - if (Object::cast_to<Node2D>(canvas_item)) - se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot(); - if (Object::cast_to<Control>(canvas_item)) - se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset(); - se->pre_drag_xform = canvas_item->get_global_transform_with_canvas(); - se->pre_drag_rect = canvas_item->_edit_get_rect(); - return; - } - if (tool == TOOL_SELECT) { - // Open a sub-scene on double-click - if (b->is_doubleclick()) { - if (canvas_item->get_filename() != "" && canvas_item != editor->get_edited_scene()) { - editor->open_request(canvas_item->get_filename()); - return; - } - } - - // Drag resize handles - drag = _get_resize_handle_drag_type(click, drag_point_from); - if (drag != DRAG_NONE) { - drag_from = transform.affine_inverse().xform(click); - se->undo_state = canvas_item->_edit_get_state(); - if (Object::cast_to<Node2D>(canvas_item)) - se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot(); - if (Object::cast_to<Control>(canvas_item)) - se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset(); - se->pre_drag_xform = canvas_item->get_global_transform_with_canvas(); - se->pre_drag_rect = canvas_item->_edit_get_rect(); - return; + // Symmetric resize + if (symmetric) { + if (drag_type == DRAG_LEFT || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_BOTTOM_LEFT) { + current_end.x = 2.0 * center.x - current_begin.x; + } else if (drag_type == DRAG_RIGHT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_RIGHT) { + current_begin.x = 2.0 * center.x - current_end.x; } - - // Drag anchor handles - Control *control = Object::cast_to<Control>(canvas_item); - if (control && show_helpers && !Object::cast_to<Container>(control->get_parent())) { - drag = _get_anchor_handle_drag_type(click, drag_point_from); - if (drag != DRAG_NONE) { - drag_from = transform.affine_inverse().xform(click); - se->undo_state = canvas_item->_edit_get_state(); - se->pre_drag_xform = canvas_item->get_global_transform_with_canvas(); - se->pre_drag_rect = canvas_item->_edit_get_rect(); - return; - } + if (drag_type == DRAG_TOP || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT) { + current_end.y = 2.0 * center.y - current_begin.y; + } else if (drag_type == DRAG_BOTTOM || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT) { + current_begin.y = 2.0 * center.y - current_end.y; } } + canvas_item->_edit_set_rect(Rect2(current_begin, current_end - current_begin)); + return true; } - // Multiple selected items - Point2 click = b->get_position(); + // Confirm resize + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { + _commit_canvas_item_state(drag_selection, TTR("Resize CanvasItem")); + drag_type = DRAG_NONE; + viewport->update(); + return true; + } - if ((b->get_alt() || tool == TOOL_MOVE) && get_item_count()) { - // Drag the nodes - _prepare_drag(click); + // Cancel a drag + if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + _restore_canvas_item_state(drag_selection); + drag_type = DRAG_NONE; viewport->update(); - return; + return true; } + } + return false; +} + +bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; + Ref<InputEventKey> k = p_event; - CanvasItem *c = NULL; - if (Cbone) { - c = Object::cast_to<CanvasItem>(ObjectDB::get_instance(Cbone->get().bone)); - if (c) - c = c->get_parent_item(); + if (drag_type == DRAG_NONE) { + //Start moving the nodes + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + if ((b->get_alt() || tool == TOOL_MOVE) && selection.size() > 0) { + drag_type = DRAG_ALL; + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = selection; + _save_canvas_item_state(drag_selection); + return true; + } } + } - Node *scene = editor->get_edited_scene(); - if (!scene) - return; - // Find the item to select - if (!c) { - Vector<_SelectResult> selection; - _find_canvas_items_at_pos(click, scene, transform, Transform2D(), selection, 1); - if (!selection.empty()) - c = selection[0].item; + if (drag_type == DRAG_ALL) { + // Move the nodes + if (m.is_valid()) { + _restore_canvas_item_state(drag_selection, true); + + drag_to = transform.affine_inverse().xform(m->get_position()); + Point2 previous_pos; + if (drag_selection.size() == 1) { + Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse(); + previous_pos = xform.xform(drag_selection[0]->_edit_get_position()); + } else { + previous_pos = _get_encompassing_rect_from_list(drag_selection).position; + } + Point2 new_pos = snap_point(previous_pos + (drag_to - drag_from), SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES); + bool single_axis = m->get_shift(); + if (single_axis) { + if (ABS(new_pos.x - previous_pos.x) > ABS(new_pos.y - previous_pos.y)) { + new_pos.y = previous_pos.y; + } else { + new_pos.x = previous_pos.x; + } + } + + bool force_no_IK = m->get_alt(); + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); - CanvasItem *cn = c; - while (cn) { - if (cn->has_meta("_edit_group_")) { - c = cn; + Node2D *node2d = Object::cast_to<Node2D>(canvas_item); + if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) { + _solve_IK(node2d, new_pos); + } else { + canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); } - cn = cn->get_parent_item(); } + return true; } - Node *n = c; - while ((n && n != scene && n->get_owner() != scene) || (n && !n->is_class("CanvasItem"))) { - n = n->get_parent(); - }; + // Confirm the move (only if it was moved) + if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && (drag_type == DRAG_ALL)) { + if (transform.affine_inverse().xform(b->get_position()) != drag_from) { + _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true); + } - if (n) { - c = Object::cast_to<CanvasItem>(n); - } else { - c = NULL; + drag_type = DRAG_NONE; + viewport->update(); + return true; } - // Select the item - additive_selection = b->get_shift(); - if (!c) { - _select_click_on_empty_area(click, additive_selection, true); - } else if (!_select_click_on_item(c, click, additive_selection, true)) { - return; + // Cancel a drag + if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) { + _restore_canvas_item_state(drag_selection, true); + drag_type = DRAG_NONE; + viewport->update(); + return true; } } - Ref<InputEventMouseMotion> m = p_event; - if (m.is_valid()) { - // Mouse motion event - _update_cursor(); - - if (box_selecting) { - // Update box selection - box_selecting_to = transform.affine_inverse().xform(m->get_position()); - viewport->update(); - return; + // Move the canvas items with the arrow keys + if (k.is_valid() && k->is_pressed() && tool == TOOL_SELECT && + (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) { + if (!k->is_echo()) { + // Start moving the canvas items with the keyboard + drag_selection = _get_edited_canvas_items(); + drag_type = DRAG_KEY_MOVE; + drag_from = Vector2(); + drag_to = Vector2(); + _save_canvas_item_state(drag_selection, true); } - if (drag == DRAG_NONE) { - bool space_pressed = Input::get_singleton()->is_key_pressed(KEY_SPACE); - bool simple_panning = EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning"); - int button = m->get_button_mask(); + if (drag_selection.size() > 0) { - // Check if any of the panning triggers are activated - bool panning_tool = (button & BUTTON_MASK_LEFT) && tool == TOOL_PAN; - bool panning_middle_button = button & BUTTON_MASK_MIDDLE; - bool panning_spacebar = (button & BUTTON_MASK_LEFT) && space_pressed; - bool panning_spacebar_simple = space_pressed && simple_panning; + _restore_canvas_item_state(drag_selection, true); - if (panning_tool || panning_middle_button || panning_spacebar || panning_spacebar_simple) { - // Pan the viewport - Point2i relative; - if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { - relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect()); + bool move_local_base = k->get_alt(); + bool move_local_base_rotated = k->get_control() || k->get_metakey(); + + Vector2 dir; + if (k->get_scancode() == KEY_UP) + dir += Vector2(0, -1); + else if (k->get_scancode() == KEY_DOWN) + dir += Vector2(0, 1); + else if (k->get_scancode() == KEY_LEFT) + dir += Vector2(-1, 0); + else if (k->get_scancode() == KEY_RIGHT) + dir += Vector2(1, 0); + if (k->get_shift()) + dir *= grid_step * Math::pow(2.0, grid_step_multiplier); + + drag_to += dir; + if (k->get_shift()) + drag_to = drag_to.snapped(grid_step * Math::pow(2.0, grid_step_multiplier)); + + Point2 previous_pos; + if (drag_selection.size() == 1) { + Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse(); + previous_pos = xform.xform(drag_selection[0]->_edit_get_position()); + } else { + previous_pos = _get_encompassing_rect_from_list(drag_selection).position; + } + + Point2 new_pos; + 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); + } else if (move_local_base) { + new_pos += drag_to; } else { - relative = m->get_relative(); + new_pos = previous_pos + (drag_to - drag_from); } + } else { + new_pos = previous_pos + (drag_to - drag_from); + } - h_scroll->set_value(h_scroll->get_value() - relative.x / zoom); - v_scroll->set_value(v_scroll->get_value() - relative.y / zoom); + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); + Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); + + Node2D *node2d = Object::cast_to<Node2D>(canvas_item); + if (node2d && se->pre_drag_bones_undo_state.size() > 0) { + _solve_IK(node2d, new_pos); + } else { + canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); + } } + } + return true; + } - return; + if (k.is_valid() && !k->is_pressed() && drag_type == DRAG_KEY_MOVE && tool == TOOL_SELECT && + (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) { + // Confirm canvas items move by arrow keys + if ((!Input::get_singleton()->is_key_pressed(KEY_UP)) && + (!Input::get_singleton()->is_key_pressed(KEY_DOWN)) && + (!Input::get_singleton()->is_key_pressed(KEY_LEFT)) && + (!Input::get_singleton()->is_key_pressed(KEY_RIGHT))) { + _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true); + drag_type = DRAG_NONE; } + viewport->update(); + return true; + } - List<Node *> selection = editor_selection->get_selected_node_list(); - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + if (k.is_valid() && (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) { + // Accept the key event in any case + return true; + } + return false; +} - 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; +bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; + Ref<InputEventKey> k = p_event; - CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; + if (drag_type == DRAG_NONE) { + if (b.is_valid() && + ((b->get_button_index() == BUTTON_RIGHT && b->get_alt() && tool == TOOL_SELECT) || + (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT))) { + // Popup the selection menu list + Point2 click = transform.affine_inverse().xform(b->get_position()); - bool dragging_bone = drag == DRAG_ALL && selection.size() == 1 && bone_ik_list.size(); + _get_canvas_items_at_pos(click, selection_results); - if (!dragging_bone) { - canvas_item->_edit_set_state(se->undo_state); //reset state and reapply - if (Object::cast_to<Node2D>(canvas_item)) - Object::cast_to<Node2D>(canvas_item)->_edit_set_pivot(se->undo_pivot); - if (Object::cast_to<Control>(canvas_item)) - Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot); - } + if (selection_results.size() == 1) { + CanvasItem *item = selection_results[0].item; + selection_results.clear(); - Vector2 dfrom = drag_from; - Vector2 dto = transform.affine_inverse().xform(m->get_position()); - if (canvas_item->has_meta("_edit_lock_")) - continue; + _select_click_on_item(item, click, b->get_shift()); - if (drag == DRAG_ROTATE) { - // Rotate the node - Vector2 center = canvas_item->get_global_transform_with_canvas().get_origin(); - { - Node2D *node = Object::cast_to<Node2D>(canvas_item); - - if (node) { - real_t angle = node->get_rotation(); - node->set_rotation(snap_angle(angle + (dfrom - center).angle_to(dto - center), angle)); - display_rotate_to = dto; - display_rotate_from = center; - viewport->update(); - } - } + return true; + } else if (!selection_results.empty()) { + // Sorts items according the their z-index + selection_results.sort(); - { - Control *node = Object::cast_to<Control>(canvas_item); + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count() - 1); - if (node) { - real_t angle = node->get_rotation(); - node->set_rotation(snap_angle(angle + (dfrom - center).angle_to(dto - center), angle)); - display_rotate_to = dto; - display_rotate_from = center; - viewport->update(); - } - } + for (int i = 0; i < selection_results.size(); i++) { + CanvasItem *item = selection_results[i].item; - continue; - } + Ref<Texture> icon; + if (item->has_meta("_editor_icon")) + icon = item->get_meta("_editor_icon"); + else + icon = get_icon(has_icon(item->get_class(), "EditorIcons") ? item->get_class() : String("Object"), "EditorIcons"); + String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path()); - bool uniform = m->get_shift(); - bool symmetric = m->get_alt(); + selection_menu->add_item(item->get_name()); + selection_menu->set_item_icon(i, icon); + selection_menu->set_item_metadata(i, node_path); + selection_menu->set_item_tooltip(i, String(item->get_name()) + "\nType: " + item->get_class() + "\nPath: " + node_path); + } - Vector2 drag_vector = - canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) - - canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom); - - switch (drag) { - case DRAG_ALL: - case DRAG_NODE_2D: - dto -= drag_from - drag_point_from; - if (uniform) { - if (ABS(dto.x - drag_point_from.x) > ABS(dto.y - drag_point_from.y)) { - dto.y = drag_point_from.y; - } else { - dto.x = drag_point_from.x; - } - } - break; + selection_menu_additive_selection = b->get_shift(); + selection_menu->set_global_position(b->get_global_position()); + selection_menu->popup(); + return true; } + } - Control *control = Object::cast_to<Control>(canvas_item); - if (control) { - // Drag and snap the anchor - Transform2D c_trans_rev = canvas_item->get_global_transform_with_canvas().affine_inverse(); + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) { + // Single item selection + Point2 click = transform.affine_inverse().xform(b->get_position()); - Vector2 anchor = c_trans_rev.xform(dto - drag_from + drag_point_from); - anchor = _position_to_anchor(control, anchor); + Node *scene = editor->get_edited_scene(); + if (!scene) + return true; - Vector2 anchor_snapped = c_trans_rev.xform(snap_point(dto - drag_from + drag_point_from, SNAP_GRID | SNAP_GUIDES | SNAP_OTHER_NODES, _get_single_item(), SNAP_NODE_PARENT | SNAP_NODE_SIDES)); - anchor_snapped = _position_to_anchor(control, anchor_snapped).snapped(Vector2(0.00001, 0.00001)); + // Find the item to select + CanvasItem *canvas_item = NULL; + Vector<_SelectResult> selection; + _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0); - bool use_y = Math::abs(drag_vector.y) > Math::abs(drag_vector.x); + for (int i = 0; i < selection.size(); i++) { + if (editor_selection->is_selected(selection[i].item)) { + // Drag the node(s) if requested + List<CanvasItem *> selection = _get_edited_canvas_items(); - switch (drag) { - case DRAG_ANCHOR_TOP_LEFT: - if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x, false); - if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y, false); - continue; - break; - case DRAG_ANCHOR_TOP_RIGHT: - if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x, false); - if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y, false); - continue; - break; - case DRAG_ANCHOR_BOTTOM_RIGHT: - if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x, false); - if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y, false); - break; - case DRAG_ANCHOR_BOTTOM_LEFT: - if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x, false); - if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y, false); - continue; - break; - case DRAG_ANCHOR_ALL: - if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x, false); - if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x, false); - if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y, false); - if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y, false); - continue; - break; + drag_type = DRAG_ALL; + drag_selection = selection; + drag_from = click; + _save_canvas_item_state(drag_selection); + + return true; } } - dfrom = drag_point_from; - dto = snap_point(dto, SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL, _get_single_item()); - - drag_vector = - canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) - - canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom); + if (!selection.empty()) + canvas_item = selection[0].item; - Rect2 local_rect = canvas_item->_edit_get_rect(); - Vector2 begin = local_rect.position; - Vector2 end = local_rect.position + local_rect.size; - Vector2 minsize = canvas_item->_edit_get_minimum_size(); + if (!canvas_item) { + // Start a box selection + if (!b->get_shift()) { + // Clear the selection if not additive + editor_selection->clear(); + viewport->update(); + }; - if (uniform) { - // Keep the height/width ratio of the item - float aspect = local_rect.size.aspect(); - switch (drag) { - case DRAG_LEFT: - drag_vector.y = -drag_vector.x / aspect; - break; - case DRAG_RIGHT: - drag_vector.y = drag_vector.x / aspect; - break; - case DRAG_TOP: - drag_vector.x = -drag_vector.y * aspect; - break; - case DRAG_BOTTOM: - drag_vector.x = drag_vector.y * aspect; - break; - case DRAG_BOTTOM_LEFT: - case DRAG_TOP_RIGHT: - if (aspect > 1.0) { // width > height, take x as reference - drag_vector.y = -drag_vector.x / aspect; - } else { // height > width, take y as reference - drag_vector.x = -drag_vector.y * aspect; - } - break; - case DRAG_BOTTOM_RIGHT: - case DRAG_TOP_LEFT: - if (aspect > 1.0) { // width > height, take x as reference - drag_vector.y = drag_vector.x / aspect; - } else { // height > width, take y as reference - drag_vector.x = drag_vector.y * aspect; - } - break; - } + drag_from = click; + drag_type = DRAG_BOX_SELECTION; + box_selecting_to = drag_from; + return true; } else { - switch (drag) { - case DRAG_RIGHT: - case DRAG_LEFT: - drag_vector.y = 0; - break; - case DRAG_TOP: - case DRAG_BOTTOM: - drag_vector.x = 0; - break; + bool still_selected = _select_click_on_item(canvas_item, click, b->get_shift()); + // Start dragging + if (still_selected) { + // Drag the node(s) if requested + List<CanvasItem *> selection = _get_edited_canvas_items(); + + drag_type = DRAG_ALL; + drag_selection = selection; + drag_from = click; + _save_canvas_item_state(drag_selection); } + // Select the item + return true; } + } + } - switch (drag) { - case DRAG_ALL: - begin += drag_vector; - end += drag_vector; - break; - case DRAG_RIGHT: - case DRAG_BOTTOM: - case DRAG_BOTTOM_RIGHT: - incend(begin.x, end.x, drag_vector.x, minsize.x, symmetric); - incend(begin.y, end.y, drag_vector.y, minsize.y, symmetric); - break; - case DRAG_TOP_LEFT: - incbeg(begin.x, end.x, drag_vector.x, minsize.x, symmetric); - incbeg(begin.y, end.y, drag_vector.y, minsize.y, symmetric); - break; - case DRAG_TOP: - case DRAG_TOP_RIGHT: - incbeg(begin.y, end.y, drag_vector.y, minsize.y, symmetric); - incend(begin.x, end.x, drag_vector.x, minsize.x, symmetric); - break; - case DRAG_LEFT: - case DRAG_BOTTOM_LEFT: - incbeg(begin.x, end.x, drag_vector.x, minsize.x, symmetric); - incend(begin.y, end.y, drag_vector.y, minsize.y, symmetric); - break; - - case DRAG_PIVOT: + if (drag_type == DRAG_BOX_SELECTION) { + if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT) { + // Confirms box selection + Node *scene = editor->get_edited_scene(); + if (scene) { + List<CanvasItem *> selitems; - if (Object::cast_to<Node2D>(canvas_item)) { - Node2D *n2d = Object::cast_to<Node2D>(canvas_item); - n2d->_edit_set_pivot(se->undo_pivot + drag_vector); - } - if (Object::cast_to<Control>(canvas_item)) { - Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot + drag_vector); - } - continue; - break; - case DRAG_NODE_2D: + Point2 bsfrom = drag_from; + Point2 bsto = box_selecting_to; + if (bsfrom.x > bsto.x) + SWAP(bsfrom.x, bsto.x); + if (bsfrom.y > bsto.y) + SWAP(bsfrom.y, bsto.y); - ERR_FAIL_COND(!Object::cast_to<Node2D>(canvas_item)); - Object::cast_to<Node2D>(canvas_item)->set_global_position(dto); - continue; - break; + _find_canvas_items_in_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems); + for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) { + editor_selection->add_node(E->get()); + } } - if (!dragging_bone) { - - local_rect.position = begin; - local_rect.size = end - begin; - canvas_item->_edit_set_rect(local_rect); - - } else { - //ok, all that had to be done was done, now solve IK - - Node2D *n2d = Object::cast_to<Node2D>(canvas_item); - Transform2D final_xform = bone_orig_xform; + drag_type = DRAG_NONE; + viewport->update(); + return true; + } - if (n2d) { + if (b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_RIGHT) { + // Cancel box selection + drag_type = DRAG_NONE; + viewport->update(); + return true; + } - float total_len = 0; - for (List<BoneIK>::Element *E = bone_ik_list.front(); E; E = E->next()) { - if (E->prev()) - total_len += E->get().len; - E->get().pos = E->get().node->get_global_transform().get_origin(); - } + if (m.is_valid()) { + // Update box selection + box_selecting_to = transform.affine_inverse().xform(m->get_position()); + viewport->update(); + return true; + } + } - { + if (k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_ESCAPE && drag_type == DRAG_NONE && tool == TOOL_SELECT) { + // Unselect everything + editor_selection->clear(); + viewport->update(); + } + return false; +} - final_xform.elements[2] += dto - dfrom; //final_xform.affine_inverse().basis_xform_inv(drag_vector); - //n2d->set_global_transform(final_xform); - } +bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { - CanvasItem *last = bone_ik_list.back()->get().node; - if (!last) + Ref<InputEventMouseMotion> m = p_event; + if (m.is_valid()) { + if (drag_type == DRAG_NONE && tool == TOOL_SELECT) { + Point2 click = transform.affine_inverse().xform(m->get_position()); + + //Checks if the hovered items changed, update the viewport if so + Vector<_SelectResult> hovering_results_tmp; + _get_canvas_items_at_pos(click, hovering_results_tmp); + hovering_results_tmp.sort(); + bool changed = false; + if (hovering_results.size() == hovering_results_tmp.size()) { + for (int i = 0; i < hovering_results.size(); i++) { + if (hovering_results[i].item != hovering_results_tmp[i].item) { + changed = true; break; - - Vector2 root_pos = last->get_global_transform().get_origin(); - Vector2 leaf_pos = final_xform.get_origin(); - - if ((leaf_pos.distance_to(root_pos)) > total_len) { - //oops dude you went too far - //print_line("TOO FAR!"); - Vector2 rel = leaf_pos - root_pos; - rel = rel.normalized() * total_len; - leaf_pos = root_pos + rel; - } - - bone_ik_list.front()->get().pos = leaf_pos; - - //print_line("BONE IK LIST "+itos(bone_ik_list.size())); - - if (bone_ik_list.size() > 2) { - int solver_iterations = 64; - float solver_k = 0.3; - - for (int i = 0; i < solver_iterations; i++) { - - for (List<BoneIK>::Element *E = bone_ik_list.front(); E; E = E->next()) { - - if (E == bone_ik_list.back()) { - - break; - } - - float len = E->next()->get().len; - - if (E->next() == bone_ik_list.back()) { - - //print_line("back"); - - Vector2 rel = E->get().pos - E->next()->get().pos; - //print_line("PREV "+E->get().pos); - Vector2 desired = E->next()->get().pos + rel.normalized() * len; - //print_line("DESIRED "+desired); - E->get().pos = E->get().pos.linear_interpolate(desired, solver_k); - //print_line("POST "+E->get().pos); - - } else if (E == bone_ik_list.front()) { - //only adjust parent - //print_line("front"); - Vector2 rel = E->next()->get().pos - E->get().pos; - //print_line("PREV "+E->next()->get().pos); - Vector2 desired = E->get().pos + rel.normalized() * len; - //print_line("DESIRED "+desired); - E->next()->get().pos = E->next()->get().pos.linear_interpolate(desired, solver_k); - //print_line("POST "+E->next()->get().pos); - } else { - - Vector2 rel = E->next()->get().pos - E->get().pos; - Vector2 cen = (E->next()->get().pos + E->get().pos) * 0.5; - rel = rel.linear_interpolate(rel.normalized() * len, solver_k); - rel *= 0.5; - E->next()->get().pos = cen + rel; - E->get().pos = cen - rel; - //print_line("mid"); - } - } - } } } + } else { + changed = true; + } - for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) { - - Node2D *n = E->get().node; - - if (!E->prev()) { - //last goes to what it was - final_xform.set_origin(n->get_global_position()); - n->set_global_transform(final_xform); + if (changed) { + hovering_results = hovering_results_tmp; + viewport->update(); + } - } else { - Vector2 rel = (E->prev()->get().node->get_global_position() - n->get_global_position()).normalized(); - Vector2 rel2 = (E->prev()->get().pos - E->get().pos).normalized(); - float rot = rel.angle_to(rel2); - if (n->get_global_transform().basis_determinant() < 0) { - //mirrored, rotate the other way - rot = -rot; - } + return true; + } + } - n->rotate(rot); - } - } + return false; +} - break; +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_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"); + } + + if (accepted) + accept_event(); + + // Handles the mouse hovering + _gui_input_hover(p_event); + + // Change the cursor + CursorShape c = CURSOR_ARROW; + switch (drag_type) { + case DRAG_NONE: + switch (tool) { + case TOOL_MOVE: + c = CURSOR_MOVE; + break; + case TOOL_EDIT_PIVOT: + c = CURSOR_CROSS; + break; + case TOOL_PAN: + c = CURSOR_DRAG; + break; + default: + break; } - } + break; + case DRAG_LEFT: + case DRAG_RIGHT: + c = CURSOR_HSIZE; + break; + case DRAG_TOP: + case DRAG_BOTTOM: + c = CURSOR_VSIZE; + break; + case DRAG_TOP_LEFT: + case DRAG_BOTTOM_RIGHT: + c = CURSOR_FDIAGSIZE; + break; + case DRAG_TOP_RIGHT: + case DRAG_BOTTOM_LEFT: + c = CURSOR_BDIAGSIZE; + break; + case DRAG_ALL: + c = CURSOR_MOVE; + break; + case DRAG_PAN: + c = CURSOR_DRAG; + default: + break; + } + viewport->set_default_cursor_shape(c); + + // Grab focus + if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) { + viewport->call_deferred("grab_focus"); } } @@ -2292,8 +2037,8 @@ void CanvasItemEditor::_draw_percentage_at_position(float p_value, Point2 p_posi void CanvasItemEditor::_draw_focus() { // Draw the focus around the base viewport - if (viewport_base->has_focus()) { - get_stylebox("Focus", "EditorStyles")->draw(viewport_base->get_canvas_item(), Rect2(Point2(), viewport_base->get_size())); + if (viewport->has_focus()) { + get_stylebox("Focus", "EditorStyles")->draw(viewport->get_canvas_item(), Rect2(Point2(), viewport->get_size())); } } @@ -2306,63 +2051,62 @@ void CanvasItemEditor::_draw_guides() { if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); for (int i = 0; i < vguides.size(); i++) { - if (drag == DRAG_V_GUIDE && i == edited_guide_index) + if (drag_type == DRAG_V_GUIDE && i == dragged_guide_index) continue; float x = xform.xform(Point2(vguides[i], 0)).x; - viewport_base->draw_line(Point2(x, 0), Point2(x, viewport_base->get_size().y), guide_color); + viewport->draw_line(Point2(x, 0), Point2(x, viewport->get_size().y), guide_color); } } if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) { Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_"); for (int i = 0; i < hguides.size(); i++) { - if (drag == DRAG_H_GUIDE && i == edited_guide_index) + if (drag_type == DRAG_H_GUIDE && i == dragged_guide_index) continue; float y = xform.xform(Point2(0, hguides[i])).y; - viewport_base->draw_line(Point2(0, y), Point2(viewport_base->get_size().x, y), guide_color); + viewport->draw_line(Point2(0, y), Point2(viewport->get_size().x, y), guide_color); } } // Dragged guide Color text_color = get_color("font_color", "Editor"); text_color.a = 0.5; - if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_V_GUIDE) { - String str = vformat("%d px", xform.affine_inverse().xform(edited_guide_pos).x); + if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_V_GUIDE) { + String str = vformat("%d px", xform.affine_inverse().xform(dragged_guide_pos).x); Ref<Font> font = get_font("font", "Label"); Size2 text_size = font->get_string_size(str); - viewport_base->draw_string(font, Point2(edited_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, text_color); - viewport_base->draw_line(Point2(edited_guide_pos.x, 0), Point2(edited_guide_pos.x, viewport_base->get_size().y), guide_color); + viewport->draw_string(font, Point2(dragged_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, text_color); + viewport->draw_line(Point2(dragged_guide_pos.x, 0), Point2(dragged_guide_pos.x, viewport->get_size().y), guide_color); } - if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_H_GUIDE) { - String str = vformat("%d px", xform.affine_inverse().xform(edited_guide_pos).y); + if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_H_GUIDE) { + String str = vformat("%d px", xform.affine_inverse().xform(dragged_guide_pos).y); Ref<Font> font = get_font("font", "Label"); Size2 text_size = font->get_string_size(str); - viewport_base->draw_string(font, Point2(RULER_WIDTH + 10, edited_guide_pos.y + text_size.y / 2 + 10), str, text_color); - viewport_base->draw_line(Point2(0, edited_guide_pos.y), Point2(viewport_base->get_size().x, edited_guide_pos.y), guide_color); + viewport->draw_string(font, Point2(RULER_WIDTH + 10, dragged_guide_pos.y + text_size.y / 2 + 10), str, text_color); + viewport->draw_line(Point2(0, dragged_guide_pos.y), Point2(viewport->get_size().x, dragged_guide_pos.y), guide_color); } } void CanvasItemEditor::_draw_rulers() { - Color graduation_color = get_color("font_color", "Editor"); - graduation_color.a = 0.5; Color bg_color = get_color("dark_color_2", "Editor"); + Color graduation_color = get_color("font_color", "Editor").linear_interpolate(bg_color, 0.5); Color font_color = get_color("font_color", "Editor"); font_color.a = 0.8; Ref<Font> font = get_font("rulers", "EditorFonts"); + bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); // The rule transform - Transform2D ruler_transform; - if (show_grid || snap_grid) { - ruler_transform = Transform2D(); - if (snap_relative && get_item_count() > 0) { - ruler_transform.translate(_find_topleftmost_point()); + Transform2D ruler_transform = Transform2D(); + if (show_grid || (is_snap_active && snap_grid)) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + if (snap_relative && selection.size() > 0) { + ruler_transform.translate(_get_encompassing_rect_from_list(selection).position); ruler_transform.scale_basis(grid_step * Math::pow(2.0, grid_step_multiplier)); } else { ruler_transform.translate(grid_offset); ruler_transform.scale_basis(grid_step * Math::pow(2.0, grid_step_multiplier)); } while ((transform * ruler_transform).get_scale().x < 50 || (transform * ruler_transform).get_scale().y < 50) { - ruler_transform.scale_basis(Point2(2, 2)); } } else { @@ -2373,7 +2117,6 @@ void CanvasItemEditor::_draw_rulers() { for (int i = 0; basic_rule * zoom < 100; i++) { basic_rule *= (i % 2) ? 2.0 : 5.0; } - ruler_transform = Transform2D(); ruler_transform.scale(Size2(basic_rule, basic_rule)); } @@ -2387,43 +2130,43 @@ void CanvasItemEditor::_draw_rulers() { minor_subdivide.scale(Size2(1.0 / minor_subdivision, 1.0 / minor_subdivision)); // First and last graduations to draw (in the ruler space) - Point2 first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Point2()); + Point2 first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Point2(RULER_WIDTH, RULER_WIDTH)); Point2 last = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(viewport->get_size()); // Draw top ruler - viewport_base->draw_rect(Rect2(Point2(RULER_WIDTH, 0), Size2(viewport->get_size().x, RULER_WIDTH)), bg_color); + viewport->draw_rect(Rect2(Point2(RULER_WIDTH, 0), Size2(viewport->get_size().x, RULER_WIDTH)), bg_color); for (int i = Math::ceil(first.x); i < last.x; i++) { Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)); if (i % (major_subdivision * minor_subdivision) == 0) { - viewport_base->draw_line(Point2(position.x + RULER_WIDTH, 0), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(position.x, 0), Point2(position.x, RULER_WIDTH), graduation_color); float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x; - viewport_base->draw_string(font, Point2(position.x + RULER_WIDTH + 2, font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + viewport->draw_string(font, Point2(position.x + 2, font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); } else { if (i % minor_subdivision == 0) { - viewport_base->draw_line(Point2(position.x + RULER_WIDTH, RULER_WIDTH * 0.33), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.33), Point2(position.x, RULER_WIDTH), graduation_color); } else { - viewport_base->draw_line(Point2(position.x + RULER_WIDTH, RULER_WIDTH * 0.66), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.66), Point2(position.x, RULER_WIDTH), graduation_color); } } } // Draw left ruler - viewport_base->draw_rect(Rect2(Point2(0, RULER_WIDTH), Size2(RULER_WIDTH, viewport->get_size().y)), bg_color); + viewport->draw_rect(Rect2(Point2(0, RULER_WIDTH), Size2(RULER_WIDTH, viewport->get_size().y)), bg_color); for (int i = Math::ceil(first.y); i < last.y; i++) { Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)); if (i % (major_subdivision * minor_subdivision) == 0) { - viewport_base->draw_line(Point2(0, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(0, position.y), Point2(RULER_WIDTH, position.y), graduation_color); float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; - viewport_base->draw_string(font, Point2(2, position.y + RULER_WIDTH + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + viewport->draw_string(font, Point2(2, position.y + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); } else { if (i % minor_subdivision == 0) { - viewport_base->draw_line(Point2(RULER_WIDTH * 0.33, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(RULER_WIDTH * 0.33, position.y), Point2(RULER_WIDTH, position.y), graduation_color); } else { - viewport_base->draw_line(Point2(RULER_WIDTH * 0.66, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(RULER_WIDTH * 0.66, position.y), Point2(RULER_WIDTH, position.y), graduation_color); } } } - viewport_base->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color); + viewport->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color); } void CanvasItemEditor::_draw_grid() { @@ -2434,8 +2177,9 @@ void CanvasItemEditor::_draw_grid() { Transform2D xform = transform.affine_inverse(); Vector2 real_grid_offset; - if (snap_relative && get_item_count() > 0) { - Vector2 topleft = _find_topleftmost_point(); + List<CanvasItem *> selection = _get_edited_canvas_items(); + if (snap_relative && selection.size() > 0) { + Vector2 topleft = _get_encompassing_rect_from_list(selection).position; real_grid_offset.x = fmod(topleft.x, grid_step.x * (real_t)Math::pow(2.0, grid_step_multiplier)); real_grid_offset.y = fmod(topleft.y, grid_step.y * (real_t)Math::pow(2.0, grid_step_multiplier)); } else { @@ -2468,81 +2212,77 @@ void CanvasItemEditor::_draw_grid() { } void CanvasItemEditor::_draw_selection() { - bool pivot_found = false; Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons"); - bool single = _get_single_item() != NULL; + Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons"); + Ref<Texture> previous_position_icon = get_icon("EditorPositionPrevious", "EditorIcons"); + RID ci = viewport->get_canvas_item(); - Map<Node *, Object *> &selection = editor_selection->get_selection(); - for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { + List<CanvasItem *> selection = _get_edited_canvas_items(false, false); - 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; + bool single = selection.size() == 1; + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - - Rect2 rect = canvas_item->_edit_get_rect(); - if (show_helpers && drag != DRAG_NONE && drag != DRAG_PIVOT) { + // Draw the previous position if we are dragging the node + if (show_helpers && + (drag_type == DRAG_ALL || drag_type == DRAG_ROTATE || + drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT || drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM || + drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT)) { const Transform2D pre_drag_xform = transform * se->pre_drag_xform; const Color pre_drag_color = Color(0.4, 0.6, 1, 0.7); - Vector2 pre_drag_endpoints[4] = { + if (canvas_item->_edit_use_rect()) { + Vector2 pre_drag_endpoints[4] = { - pre_drag_xform.xform(se->pre_drag_rect.position), - pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)), - pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size), - pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y)) - }; + pre_drag_xform.xform(se->pre_drag_rect.position), + pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)), + pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size), + pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y)) + }; - for (int i = 0; i < 4; i++) { - viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2); + for (int i = 0; i < 4; i++) { + viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2); + } + } else { + viewport->draw_texture(previous_position_icon, (pre_drag_xform.xform(Point2()) - (previous_position_icon->get_size() / 2)).floor()); } } Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); - VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform); - - Vector2 endpoints[4] = { - xform.xform(rect.position), - xform.xform(rect.position + Vector2(rect.size.x, 0)), - xform.xform(rect.position + rect.size), - xform.xform(rect.position + Vector2(0, rect.size.y)) - }; + // Draw the selected items position / surrounding boxes + if (canvas_item->_edit_use_rect()) { + Rect2 rect = canvas_item->_edit_get_rect(); + Vector2 endpoints[4] = { + xform.xform(rect.position), + xform.xform(rect.position + Vector2(rect.size.x, 0)), + xform.xform(rect.position + rect.size), + xform.xform(rect.position + Vector2(0, rect.size.y)) + }; - Color c = Color(1, 0.6, 0.4, 0.7); + Color c = Color(1, 0.6, 0.4, 0.7); - VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D()); + for (int i = 0; i < 4; i++) { + viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2); + } + } else { - for (int i = 0; i < 4; i++) { - viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2); + Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin()); + viewport->draw_set_transform_matrix(transform); + viewport->draw_texture(position_icon, -(position_icon->get_size() / 2)); + viewport->draw_set_transform_matrix(Transform2D()); } if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks - - Node2D *node2d = Object::cast_to<Node2D>(canvas_item); - if (node2d) { - if (node2d->_edit_use_pivot()) { - viewport->draw_texture(pivot_icon, xform.get_origin() + (-pivot_icon->get_size() / 2).floor()); - can_move_pivot = true; - pivot_found = true; - } + // Draw the pivot + if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/ + viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor()); } Control *control = Object::cast_to<Control>(canvas_item); if (control) { - Vector2 pivot_ofs = control->get_pivot_offset(); - if (pivot_ofs != Vector2()) { - viewport->draw_texture(pivot_icon, xform.xform(pivot_ofs) + (-pivot_icon->get_size() / 2).floor()); - } - can_move_pivot = true; - pivot_found = true; - 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); @@ -2561,10 +2301,9 @@ void CanvasItemEditor::_draw_selection() { anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i])); } - Map<Node *, Object *> &selection = editor_selection->get_selection(); // Get which anchor is dragged int dragged_anchor = -1; - switch (drag) { + switch (drag_type) { case DRAG_ANCHOR_ALL: case DRAG_ANCHOR_TOP_LEFT: dragged_anchor = 0; @@ -2578,6 +2317,8 @@ void CanvasItemEditor::_draw_selection() { case DRAG_ANCHOR_BOTTOM_LEFT: dragged_anchor = 3; break; + default: + break; } if (dragged_anchor >= 0) { @@ -2639,56 +2380,65 @@ void CanvasItemEditor::_draw_selection() { node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * control->get_parent_area_size().width + control->get_margin(MARGIN_RIGHT); node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * control->get_parent_area_size().height + control->get_margin(MARGIN_BOTTOM); - switch (drag) { + Point2 start, end; + switch (drag_type) { case DRAG_LEFT: 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); case DRAG_ALL: - Point2 start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); - Point2 end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); + 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); _draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP); viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); break; + default: + break; } - switch (drag) { + switch (drag_type) { case DRAG_RIGHT: 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); case DRAG_ALL: - Point2 start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); - Point2 end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); + 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); _draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM); viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); break; + default: + break; } - switch (drag) { + switch (drag_type) { case DRAG_TOP: 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); case DRAG_ALL: - Point2 start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); - Point2 end = start - Vector2(0, control->get_margin(MARGIN_TOP)); + 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)); _draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT); viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); break; + default: + break; } - switch (drag) { + switch (drag_type) { case DRAG_BOTTOM: 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); case DRAG_ALL: - Point2 start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); - Point2 end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); + 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)); _draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT); viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1); break; + default: + break; } - switch (drag) { + switch (drag_type) { //Draw the ghost rect if the node if rotated/scaled case DRAG_LEFT: case DRAG_TOP_LEFT: @@ -2704,14 +2454,22 @@ void CanvasItemEditor::_draw_selection() { viewport->draw_rect(parent_transform.xform(rect), color_base, false); } break; + default: + break; } } } - if (tool == TOOL_SELECT) { - + if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) { + Rect2 rect = canvas_item->_edit_get_rect(); + Vector2 endpoints[4] = { + xform.xform(rect.position), + xform.xform(rect.position + Vector2(rect.size.x, 0)), + xform.xform(rect.position + rect.size), + xform.xform(rect.position + Vector2(0, rect.size.y)) + }; for (int i = 0; i < 4; i++) { - + // Draw the resize handles int prev = (i + 3) % 4; int next = (i + 1) % 4; @@ -2728,9 +2486,9 @@ void CanvasItemEditor::_draw_selection() { } } } - pivot_button->set_disabled(!pivot_found); - if (box_selecting) { + if (drag_type == DRAG_BOX_SELECTION) { + // Draw the dragging box Point2 bsfrom = transform.xform(drag_from); Point2 bsto = transform.xform(box_selecting_to); @@ -2738,33 +2496,82 @@ void CanvasItemEditor::_draw_selection() { } Color rotate_color(0.4, 0.7, 1.0, 0.8); - if (drag == DRAG_ROTATE) { - VisualServer::get_singleton()->canvas_item_add_line(ci, transform.xform(display_rotate_from), transform.xform(display_rotate_to), rotate_color); + 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); } } -void CanvasItemEditor::_draw_axis() { +void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) { + // Draw a line going through the whole screen from a vector RID ci = viewport->get_canvas_item(); + Vector<Point2> points; + Point2 from = transform.xform(p_from); + Point2 to = transform.xform(p_to); + Size2 viewport_size = viewport->get_size(); + + if (to.x == from.x) { + // Vertical line + points.push_back(Point2(to.x, 0)); + points.push_back(Point2(to.x, viewport_size.y)); + } else if (to.y == from.y) { + // Horizontal line + points.push_back(Point2(0, to.y)); + points.push_back(Point2(viewport_size.x, to.y)); + } else { + float y_for_zero_x = (to.y * from.x - from.y * to.x) / (from.x - to.x); + float x_for_zero_y = (to.x * from.y - from.x * to.y) / (from.y - to.y); + float y_for_viewport_x = ((to.y - from.y) * (viewport_size.x - from.x)) / (to.x - from.x) + from.y; + float x_for_viewport_y = ((to.x - from.x) * (viewport_size.y - from.y)) / (to.y - from.y) + from.x; // faux + + //bool start_set = false; + if (y_for_zero_x >= 0 && y_for_zero_x <= viewport_size.y) { + points.push_back(Point2(0, y_for_zero_x)); + } + if (x_for_zero_y >= 0 && x_for_zero_y <= viewport_size.x) { + points.push_back(Point2(x_for_zero_y, 0)); + } + if (y_for_viewport_x >= 0 && y_for_viewport_x <= viewport_size.y) { + points.push_back(Point2(viewport_size.x, y_for_viewport_x)); + } + if (x_for_viewport_y >= 0 && x_for_viewport_y <= viewport_size.x) { + points.push_back(Point2(x_for_viewport_y, viewport_size.y)); + } + } + if (points.size() >= 2) { + VisualServer::get_singleton()->canvas_item_add_line(ci, points[0], points[1], p_color); + } +} - Color x_axis_color(1.0, 0.4, 0.4, 0.6); - Color y_axis_color(0.4, 1.0, 0.4, 0.6); - Color area_axis_color(0.4, 0.4, 1.0, 0.4); +void CanvasItemEditor::_draw_axis() { - Point2 origin = transform.get_origin(); - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(0, origin.y), Point2(viewport->get_size().x, origin.y), x_axis_color); - VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(origin.x, 0), Point2(origin.x, viewport->get_size().y), y_axis_color); + if (show_origin) { - Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + Color x_axis_color(1.0, 0.4, 0.4, 0.6); + Color y_axis_color(0.4, 1.0, 0.4, 0.6); - Vector2 screen_endpoints[4] = { - transform.xform(Vector2(0, 0)), - transform.xform(Vector2(screen_size.width, 0)), - transform.xform(Vector2(screen_size.width, screen_size.height)), - transform.xform(Vector2(0, screen_size.height)) - }; + _draw_straight_line(Point2(), Point2(1, 0), x_axis_color); + _draw_straight_line(Point2(), Point2(0, 1), y_axis_color); + } + + if (show_viewport) { + + RID ci = viewport->get_canvas_item(); + + Color area_axis_color(0.4, 0.4, 1.0, 0.4); - for (int i = 0; i < 4; i++) { - VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color); + Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + + Vector2 screen_endpoints[4] = { + transform.xform(Vector2(0, 0)), + transform.xform(Vector2(screen_size.width, 0)), + transform.xform(Vector2(screen_size.width, screen_size.height)), + transform.xform(Vector2(0, screen_size.height)) + }; + + for (int i = 0; i < 4; i++) { + VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color); + } } } @@ -2776,43 +2583,52 @@ void CanvasItemEditor::_draw_bones() { Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1"); Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2"); Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color"); + Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color"); Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color"); + int bone_outline_size = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size"); - for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { + for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { - E->get().from = Vector2(); - E->get().to = Vector2(); + Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from)); + Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to)); - Node2D *n2d = Object::cast_to<Node2D>(ObjectDB::get_instance(E->get().bone)); - if (!n2d) + if (!from_node->is_inside_tree()) + continue; //may have been removed + if (!from_node) continue; - if (!n2d->get_parent()) + if (!to_node && E->get().length == 0) continue; - CanvasItem *pi = n2d->get_parent_item(); + Vector2 from = transform.xform(from_node->get_global_position()); + Vector2 to; - Node2D *pn2d = Object::cast_to<Node2D>(n2d->get_parent()); - - if (!pn2d) - continue; - - Vector2 from = transform.xform(pn2d->get_global_position()); - Vector2 to = transform.xform(n2d->get_global_position()); - - E->get().from = from; - E->get().to = to; + if (to_node) + to = transform.xform(to_node->get_global_position()); + else + to = transform.xform(from_node->get_global_transform().xform(Vector2(E->get().length, 0))); Vector2 rel = to - from; Vector2 relt = rel.tangent().normalized() * bone_width; + Vector2 reln = rel.normalized(); + Vector2 reltn = relt.normalized(); Vector<Vector2> bone_shape; bone_shape.push_back(from); bone_shape.push_back(from + rel * 0.2 + relt); bone_shape.push_back(to); bone_shape.push_back(from + rel * 0.2 - relt); + + Vector<Vector2> bone_shape_outline; + bone_shape_outline.push_back(from + (-reln - reltn) * bone_outline_size); + bone_shape_outline.push_back(from + (-reln + reltn) * bone_outline_size); + bone_shape_outline.push_back(from + rel * 0.2 + relt + reltn * bone_outline_size); + bone_shape_outline.push_back(to + (reln + reltn) * bone_outline_size); + bone_shape_outline.push_back(to + (reln - reltn) * bone_outline_size); + bone_shape_outline.push_back(from + rel * 0.2 - relt - reltn * bone_outline_size); + Vector<Color> colors; - if (pi->has_meta("_edit_ik_")) { + if (from_node->has_meta("_edit_ik_")) { colors.push_back(bone_ik_color); colors.push_back(bone_ik_color); @@ -2825,98 +2641,221 @@ void CanvasItemEditor::_draw_bones() { colors.push_back(bone_color2); } + Vector<Color> outline_colors; + + if (editor_selection->is_selected(from_node)) { + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + outline_colors.push_back(bone_selected_color); + } else { + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + outline_colors.push_back(bone_outline_color); + } + + VisualServer::get_singleton()->canvas_item_add_polygon(ci, bone_shape_outline, outline_colors); VisualServer::get_singleton()->canvas_item_add_primitive(ci, bone_shape, colors, Vector<Vector2>(), RID()); + } + } +} - if (editor_selection->is_selected(pi)) { - for (int i = 0; i < bone_shape.size(); i++) { +void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { + ERR_FAIL_COND(!p_node); - VisualServer::get_singleton()->canvas_item_add_line(ci, bone_shape[i], bone_shape[(i + 1) % bone_shape.size()], bone_selected_color, 2); - } + Node *scene = editor->get_edited_scene(); + if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner())) + return; + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); + if (canvas_item && !canvas_item->is_visible()) + return; + + Transform2D parent_xform = p_parent_xform; + Transform2D canvas_xform = p_canvas_xform; + + if (canvas_item && !canvas_item->is_set_as_toplevel()) { + parent_xform = parent_xform * canvas_item->get_transform(); + } else { + CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); + parent_xform = Transform2D(); + canvas_xform = cl ? cl->get_transform() : p_canvas_xform; + } + + for (int i = p_node->get_child_count() - 1; i >= 0; i--) { + _draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform); + } + + if (canvas_item && !canvas_item->_edit_use_rect() && !editor_selection->is_selected(canvas_item)) { + Transform2D xform = transform * canvas_xform * parent_xform; + + // Draw the node's position + Ref<Texture> position_icon = get_icon("EditorPositionUnselected", "EditorIcons"); + Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin()); + viewport->draw_set_transform_matrix(transform); + viewport->draw_texture(position_icon, -position_icon->get_size() / 2, Color(1.0, 1.0, 1.0, 0.5)); + viewport->draw_set_transform_matrix(Transform2D()); + } +} + +void CanvasItemEditor::_draw_hover() { + List<Rect2> previous_rects; + + for (int i = 0; i < hovering_results.size(); i++) { + // Draw the node's name and icon + CanvasItem *canvas_item = hovering_results[i].item; + + if (canvas_item->_edit_use_rect()) + continue; + + Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); + + // Get the resources + Ref<Texture> node_icon; + if (has_icon(canvas_item->get_class(), "EditorIcons")) + node_icon = get_icon(canvas_item->get_class(), "EditorIcons"); + else + node_icon = get_icon("Object", "EditorIcons"); + Ref<Font> font = get_font("font", "Label"); + String node_name = canvas_item->get_name(); + Size2 node_name_size = font->get_string_size(node_name); + Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3)); + + Point2 pos = xform.get_origin() - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4); + // Rectify the position to avoid overlaping items + for (List<Rect2>::Element *E = previous_rects.front(); E; E = E->next()) { + if (E->get().intersects(Rect2(pos, item_size))) { + pos.y = E->get().get_position().y - item_size.y; } } + + previous_rects.push_back(Rect2(pos, item_size)); + + // Draw the node icon and name + viewport->draw_texture(node_icon, pos, Color(1.0, 1.0, 1.0, 0.5)); + viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, Color(1.0, 1.0, 1.0, 0.5)); } } -void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_xform) { +void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { ERR_FAIL_COND(!p_node); - RID viewport_ci = viewport->get_canvas_item(); + Node *scene = editor->get_edited_scene(); + if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner())) + return; + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); + if (canvas_item && !canvas_item->is_visible()) + return; - Transform2D transform_ci = p_xform; - CanvasItem *ci = Object::cast_to<CanvasItem>(p_node); - if (ci) - transform_ci = transform_ci * ci->get_transform(); + Transform2D parent_xform = p_parent_xform; + Transform2D canvas_xform = p_canvas_xform; + + if (canvas_item && !canvas_item->is_set_as_toplevel()) { + parent_xform = parent_xform * canvas_item->get_transform(); + } else { + CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); + parent_xform = Transform2D(); + canvas_xform = cl ? cl->get_transform() : p_canvas_xform; + } for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - _draw_locks_and_groups(p_node->get_child(i), transform_ci); + _draw_locks_and_groups(p_node->get_child(i), parent_xform, canvas_xform); } - if (ci) { + RID viewport_canvas_item = viewport->get_canvas_item(); + if (canvas_item) { + float offset = 0; + Ref<Texture> lock = get_icon("LockViewport", "EditorIcons"); if (p_node->has_meta("_edit_lock_")) { - lock->draw(viewport_ci, transform_ci.xform(Point2(0, 0))); + lock->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0)); + offset += lock->get_size().x; } Ref<Texture> group = get_icon("GroupViewport", "EditorIcons"); - if (ci->has_meta("_edit_group_")) { - Vector2 ofs = transform_ci.xform(Point2(0, 0)); - if (ci->has_meta("_edit_lock_")) - ofs = Point2(ofs.x + lock->get_size().x, ofs.y); - group->draw(viewport_ci, ofs); + if (canvas_item->has_meta("_edit_group_")) { + group->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0)); + //offset += group->get_size().x; } } } -void CanvasItemEditor::_build_bones_list(Node *p_node) { - ERR_FAIL_COND(!p_node); +bool CanvasItemEditor::_build_bones_list(Node *p_node) { + ERR_FAIL_COND_V(!p_node, false); + + bool has_child_bones = false; for (int i = 0; i < p_node->get_child_count(); i++) { - _build_bones_list(p_node->get_child(i)); + if (_build_bones_list(p_node->get_child(i))) { + has_child_bones = true; + } + } + + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); + Node *scene = editor->get_edited_scene(); + if (!canvas_item || !canvas_item->is_visible() || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner()))) { + return false; } - CanvasItem *c = Object::cast_to<CanvasItem>(p_node); - if (c && c->is_visible_in_tree()) { - if (c->has_meta("_edit_bone_")) { + Node *parent = canvas_item->get_parent(); - ObjectID id = c->get_instance_id(); - if (!bone_list.has(id)) { - BoneList bone; - bone.bone = id; - bone_list[id] = bone; + if (Object::cast_to<Bone2D>(canvas_item)) { + if (Object::cast_to<Bone2D>(parent)) { + // Add as bone->parent relationship + BoneKey bk; + bk.from = parent->get_instance_id(); + bk.to = canvas_item->get_instance_id(); + if (!bone_list.has(bk)) { + BoneList b; + b.length = 0; + bone_list[bk] = b; } - bone_list[id].last_pass = bone_last_frame; + bone_list[bk].last_pass = bone_last_frame; } - } -} -void CanvasItemEditor::_get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform) { - ERR_FAIL_COND(!p_node); + if (!has_child_bones) { + // Add a last bone if the Bone2D has no Bone2D child + BoneKey bk; + bk.from = canvas_item->get_instance_id(); + bk.to = 0; + if (!bone_list.has(bk)) { + BoneList b; + b.length = 0; + bone_list[bk] = b; + } + bone_list[bk].last_pass = bone_last_frame; + } - for (int i = 0; i < p_node->get_child_count(); i++) { - _get_encompassing_rect(p_node->get_child(i), r_rect, p_xform); + return true; } - CanvasItem *c = Object::cast_to<CanvasItem>(p_node); - if (c && c->is_visible_in_tree()) { - Rect2 rect = c->_edit_get_rect(); - Transform2D xform = p_xform * c->get_transform(); - r_rect.expand_to(xform.xform(rect.position)); - r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); - r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); - r_rect.expand_to(xform.xform(rect.position + rect.size)); + if (canvas_item->has_meta("_edit_bone_")) { + // Add a "custom bone" + BoneKey bk; + bk.from = parent->get_instance_id(); + bk.to = canvas_item->get_instance_id(); + if (!bone_list.has(bk)) { + BoneList b; + b.length = 0; + bone_list[bk] = b; + } + bone_list[bk].last_pass = bone_last_frame; } -} -void CanvasItemEditor::_draw_viewport_base() { - if (show_rulers) - _draw_rulers(); - if (show_guides) - _draw_guides(); - _draw_focus(); + return false; } void CanvasItemEditor::_draw_viewport() { + // Update the transform + transform = Transform2D(); + transform.scale_basis(Size2(zoom, zoom)); + transform.elements[2] = -view_offset * zoom; + editor->get_scene_root()->set_global_canvas_transform(transform); // hide/show buttons depending on the selection bool all_locked = true; @@ -2947,13 +2886,13 @@ void CanvasItemEditor::_draw_viewport() { group_button->set_disabled(selection.empty()); ungroup_button->set_visible(all_group); - _update_scrollbars(); - _draw_grid(); _draw_selection(); _draw_axis(); - if (editor->get_edited_scene()) - _draw_locks_and_groups(editor->get_edited_scene(), transform); + if (editor->get_edited_scene()) { + _draw_locks_and_groups(editor->get_edited_scene()); + _draw_invisible_nodes_positions(editor->get_edited_scene()); + } RID ci = viewport->get_canvas_item(); VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D()); @@ -2968,76 +2907,79 @@ void CanvasItemEditor::_draw_viewport() { } _draw_bones(); + if (show_rulers) + _draw_rulers(); + if (show_guides) + _draw_guides(); + _draw_focus(); + _draw_hover(); } void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_PHYSICS_PROCESS) { - EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels")); - List<Node *> selection = editor_selection->get_selected_node_list(); - - bool all_control = true; - bool has_control = false; - - 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 (Object::cast_to<Control>(canvas_item)) - has_control = true; - else - all_control = false; + int nb_control = 0; + int nb_having_pivot = 0; + // Update the viewport if the canvas_item changes + List<CanvasItem *> selection = _get_edited_canvas_items(); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); - if (!se) - continue; - Rect2 r = canvas_item->_edit_get_rect(); + Rect2 rect; + if (canvas_item->_edit_use_rect()) { + rect = canvas_item->_edit_get_rect(); + } else { + rect = Rect2(); + } Transform2D xform = canvas_item->get_transform(); - if (r != se->prev_rect || xform != se->prev_xform) { + if (rect != se->prev_rect || xform != se->prev_xform) { viewport->update(); - se->prev_rect = r; + se->prev_rect = rect; se->prev_xform = xform; } - if (Object::cast_to<Control>(canvas_item)) { + Control *control = Object::cast_to<Control>(canvas_item); + if (control) { float anchors[4]; Vector2 pivot; - pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset(); - anchors[MARGIN_LEFT] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_LEFT); - anchors[MARGIN_RIGHT] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_RIGHT); - anchors[MARGIN_TOP] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_TOP); - anchors[MARGIN_BOTTOM] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_BOTTOM); + pivot = control->get_pivot_offset(); + anchors[MARGIN_LEFT] = control->get_anchor(MARGIN_LEFT); + anchors[MARGIN_RIGHT] = control->get_anchor(MARGIN_RIGHT); + anchors[MARGIN_TOP] = control->get_anchor(MARGIN_TOP); + anchors[MARGIN_BOTTOM] = control->get_anchor(MARGIN_BOTTOM); if (pivot != se->prev_pivot || anchors[MARGIN_LEFT] != se->prev_anchors[MARGIN_LEFT] || anchors[MARGIN_RIGHT] != se->prev_anchors[MARGIN_RIGHT] || anchors[MARGIN_TOP] != se->prev_anchors[MARGIN_TOP] || anchors[MARGIN_BOTTOM] != se->prev_anchors[MARGIN_BOTTOM]) { - viewport->update(); - viewport_base->update(); se->prev_pivot = pivot; se->prev_anchors[MARGIN_LEFT] = anchors[MARGIN_LEFT]; se->prev_anchors[MARGIN_RIGHT] = anchors[MARGIN_RIGHT]; se->prev_anchors[MARGIN_TOP] = anchors[MARGIN_TOP]; se->prev_anchors[MARGIN_BOTTOM] = anchors[MARGIN_BOTTOM]; + viewport->update(); } + nb_control++; + } + + if (canvas_item->_edit_use_pivot()) { + nb_having_pivot++; } } - if (all_control && has_control) - presets_menu->show(); - else - presets_menu->hide(); + // Activate / Deactivate the pivot tool + pivot_button->set_disabled(nb_having_pivot == 0); - for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { + // Show / Hide the layout button + presets_menu->set_visible(nb_control > 0 && nb_control == selection.size()); - Object *b = ObjectDB::get_instance(E->get().bone); + // Update the viewport if bones changes + for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { + + Object *b = ObjectDB::get_instance(E->key().from); if (!b) { viewport->update(); @@ -3045,13 +2987,22 @@ void CanvasItemEditor::_notification(int p_what) { } Node2D *b2 = Object::cast_to<Node2D>(b); - if (!b2) { + if (!b2 || !b2->is_inside_tree()) { continue; } - if (b2->get_global_transform() != E->get().xform) { + Transform2D global_xform = b2->get_global_transform(); + + if (global_xform != E->get().xform) { - E->get().xform = b2->get_global_transform(); + E->get().xform = global_xform; + viewport->update(); + } + + Bone2D *bone = Object::cast_to<Bone2D>(b); + if (bone && bone->get_default_length() != E->get().length) { + + E->get().length = bone->get_default_length(); viewport->update(); } } @@ -3067,12 +3018,19 @@ void CanvasItemEditor::_notification(int p_what) { AnimationPlayerEditor::singleton->get_key_editor()->connect("visibility_changed", this, "_keying_changed"); _keying_changed(); + get_tree()->connect("node_added", this, "_tree_changed", varray()); + get_tree()->connect("node_removed", this, "_tree_changed", varray()); } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { select_sb->set_texture(get_icon("EditorRect2D", "EditorIcons")); } + if (p_what == NOTIFICATION_EXIT_TREE) { + get_tree()->disconnect("node_added", this, "_tree_changed"); + get_tree()->disconnect("node_removed", this, "_tree_changed"); + } + if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { select_button->set_icon(get_icon("ToolSelect", "EditorIcons")); list_select_button->set_icon(get_icon("ListSelect", "EditorIcons")); @@ -3150,108 +3108,140 @@ void CanvasItemEditor::_notification(int p_what) { void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { - drag = DRAG_NONE; + drag_type = DRAG_NONE; // Clear the selection editor_selection->clear(); //_clear_canvas_items(); editor_selection->add_node(p_canvas_item); - //_add_canvas_item(p_canvas_item); - viewport->update(); - viewport_base->update(); } -void CanvasItemEditor::_update_scrollbars() { - - updating_scroll = true; - - if (show_rulers) - viewport_scrollable->set_begin(Point2(RULER_WIDTH, RULER_WIDTH)); - else - viewport_scrollable->set_begin(Point2()); - - Size2 size = viewport->get_size(); - Size2 hmin = h_scroll->get_minimum_size(); - Size2 vmin = v_scroll->get_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, 0)); - v_scroll->set_end(Point2(size.width, size.height)); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); +void CanvasItemEditor::_queue_update_bone_list() { - Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + if (bone_list_dirty) + return; - Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height)); + call_deferred("_update_bone_list"); + bone_list_dirty = true; +} - Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); +void CanvasItemEditor::_update_bone_list() { bone_last_frame++; if (editor->get_edited_scene()) { _build_bones_list(editor->get_edited_scene()); - _get_encompassing_rect(editor->get_edited_scene(), canvas_item_rect, Transform2D()); } - List<Map<ObjectID, BoneList>::Element *> bone_to_erase; - - for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { - + List<Map<BoneKey, BoneList>::Element *> bone_to_erase; + for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { if (E->get().last_pass != bone_last_frame) { bone_to_erase.push_back(E); + continue; } - } + Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E->key().from)); + if (!node || !node->is_inside_tree() || (node != get_tree()->get_edited_scene_root() && !get_tree()->get_edited_scene_root()->is_a_parent_of(node))) { + bone_to_erase.push_back(E); + continue; + } + } while (bone_to_erase.size()) { bone_list.erase(bone_to_erase.front()->get()); bone_to_erase.pop_front(); } + bone_list_dirty = false; +} + +void CanvasItemEditor::_tree_changed(Node *) { + _queue_update_bone_list(); +} + +void CanvasItemEditor::_update_scrollbars() { + + updating_scroll = true; + + // Move the zoom buttons + Point2 zoom_hb_begin = Point2(5, 5); + zoom_hb_begin += (show_rulers) ? Point2(RULER_WIDTH, RULER_WIDTH) : Point2(); + zoom_hb->set_begin(zoom_hb_begin); + + // Move and resize the scrollbars + Size2 size = viewport->get_size(); + Size2 hmin = h_scroll->get_minimum_size(); + Size2 vmin = v_scroll->get_minimum_size(); + + v_scroll->set_begin(Point2(size.width - vmin.width, (show_rulers) ? RULER_WIDTH : 0)); + v_scroll->set_end(Point2(size.width, size.height)); + + h_scroll->set_begin(Point2((show_rulers) ? RULER_WIDTH : 0, size.height - hmin.height)); + h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - //expand area so it's easier to do animations and stuff at 0,0 + // Get the visible frame + Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height)); + + _queue_update_bone_list(); + + // Calculate scrollable area + Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); + if (editor->get_edited_scene()) { + Rect2 content_rect = _get_encompassing_rect(editor->get_edited_scene()); + canvas_item_rect.expand_to(content_rect.position); + canvas_item_rect.expand_to(content_rect.position + content_rect.size); + } canvas_item_rect.size += screen_rect * 2; canvas_item_rect.position -= screen_rect; - Point2 ofs; + // Constraints the view offset and updates the scrollbars + Point2 begin = canvas_item_rect.position; + Point2 end = canvas_item_rect.position + canvas_item_rect.size - local_rect.size / zoom; if (canvas_item_rect.size.height <= (local_rect.size.y / zoom)) { + if (ABS(begin.y - previous_update_view_offset.y) < ABS(begin.y - view_offset.y)) { + view_offset.y = previous_update_view_offset.y; + } + v_scroll->hide(); - ofs.y = canvas_item_rect.position.y; } else { - - v_scroll->show(); - v_scroll->set_min(canvas_item_rect.position.y); - v_scroll->set_max(canvas_item_rect.position.y + canvas_item_rect.size.y); - v_scroll->set_page(local_rect.size.y / zoom); - if (first_update) { - //so 0,0 is visible - v_scroll->set_value(-10); - h_scroll->set_value(-10); - first_update = false; + if (view_offset.y > end.y && view_offset.y > previous_update_view_offset.y) { + view_offset.y = MAX(end.y, previous_update_view_offset.y); + } + if (view_offset.y < begin.y && view_offset.y < previous_update_view_offset.y) { + view_offset.y = MIN(begin.y, previous_update_view_offset.y); } - ofs.y = v_scroll->get_value(); + v_scroll->show(); + v_scroll->set_min(MIN(view_offset.y, begin.y)); + v_scroll->set_max(MAX(view_offset.y, end.y) + screen_rect.y); + v_scroll->set_page(screen_rect.y); } if (canvas_item_rect.size.width <= (local_rect.size.x / zoom)) { + if (ABS(begin.x - previous_update_view_offset.x) < ABS(begin.x - view_offset.x)) { + view_offset.x = previous_update_view_offset.x; + } h_scroll->hide(); - ofs.x = canvas_item_rect.position.x; } else { + if (view_offset.x > end.x && view_offset.x > previous_update_view_offset.x) { + view_offset.x = MAX(end.x, previous_update_view_offset.x); + } + if (view_offset.x < begin.x && view_offset.x < previous_update_view_offset.x) { + view_offset.x = MIN(begin.x, previous_update_view_offset.x); + } h_scroll->show(); - h_scroll->set_min(canvas_item_rect.position.x); - h_scroll->set_max(canvas_item_rect.position.x + canvas_item_rect.size.x); - h_scroll->set_page(local_rect.size.x / zoom); - ofs.x = h_scroll->get_value(); + h_scroll->set_min(MIN(view_offset.x, begin.x)); + h_scroll->set_max(MAX(view_offset.x, end.x) + screen_rect.x); + h_scroll->set_page(screen_rect.x); } - //transform=Matrix32(); - transform.elements[2] = -ofs * zoom; - - editor->get_scene_root()->set_global_canvas_transform(transform); + // Calculate scrollable area + v_scroll->set_value(view_offset.y); + h_scroll->set_value(view_offset.x); + previous_update_view_offset = view_offset; updating_scroll = false; - - //transform.scale_basis(Vector2(zoom,zoom)); } void CanvasItemEditor::_update_scroll(float) { @@ -3259,62 +3249,45 @@ void CanvasItemEditor::_update_scroll(float) { if (updating_scroll) return; - Point2 ofs; - ofs.x = h_scroll->get_value(); - ofs.y = v_scroll->get_value(); - - //current_window->set_scroll(-ofs); - - transform = Transform2D(); - - transform.scale_basis(Size2(zoom, zoom)); - transform.elements[2] = -ofs; - - editor->get_scene_root()->set_global_canvas_transform(transform); - + view_offset.x = h_scroll->get_value(); + view_offset.y = v_scroll->get_value(); viewport->update(); - viewport_base->update(); } void CanvasItemEditor::_set_anchors_and_margins_preset(Control::LayoutPreset p_preset) { 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 *c = Object::cast_to<Control>(E->get()); - - undo_redo->add_do_method(c, "set_anchors_preset", p_preset); - switch (p_preset) { - case PRESET_TOP_LEFT: - case PRESET_TOP_RIGHT: - case PRESET_BOTTOM_LEFT: - case PRESET_BOTTOM_RIGHT: - case PRESET_CENTER_LEFT: - case PRESET_CENTER_TOP: - case PRESET_CENTER_RIGHT: - case PRESET_CENTER_BOTTOM: - case PRESET_CENTER: - undo_redo->add_do_method(c, "set_margins_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE); - break; - case PRESET_LEFT_WIDE: - case PRESET_TOP_WIDE: - case PRESET_RIGHT_WIDE: - case PRESET_BOTTOM_WIDE: - case PRESET_VCENTER_WIDE: - case PRESET_HCENTER_WIDE: - case PRESET_WIDE: - undo_redo->add_do_method(c, "set_margins_preset", p_preset, Control::PRESET_MODE_MINSIZE); - break; + Control *control = Object::cast_to<Control>(E->get()); + if (control) { + undo_redo->add_do_method(control, "set_anchors_preset", p_preset); + switch (p_preset) { + case PRESET_TOP_LEFT: + case PRESET_TOP_RIGHT: + case PRESET_BOTTOM_LEFT: + case PRESET_BOTTOM_RIGHT: + case PRESET_CENTER_LEFT: + case PRESET_CENTER_TOP: + case PRESET_CENTER_RIGHT: + case PRESET_CENTER_BOTTOM: + case PRESET_CENTER: + undo_redo->add_do_method(control, "set_margins_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE); + break; + case PRESET_LEFT_WIDE: + case PRESET_TOP_WIDE: + case PRESET_RIGHT_WIDE: + case PRESET_BOTTOM_WIDE: + case PRESET_VCENTER_WIDE: + case PRESET_HCENTER_WIDE: + case PRESET_WIDE: + undo_redo->add_do_method(control, "set_margins_preset", p_preset, Control::PRESET_MODE_MINSIZE); + break; + } + undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); } - undo_redo->add_undo_method(c, "set_anchor", MARGIN_LEFT, c->get_anchor(MARGIN_LEFT)); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_TOP, c->get_anchor(MARGIN_TOP)); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_RIGHT, c->get_anchor(MARGIN_RIGHT)); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_BOTTOM, c->get_anchor(MARGIN_BOTTOM)); - undo_redo->add_undo_method(c, "set_margin", MARGIN_LEFT, c->get_margin(MARGIN_LEFT)); - undo_redo->add_undo_method(c, "set_margin", MARGIN_TOP, c->get_margin(MARGIN_TOP)); - undo_redo->add_undo_method(c, "set_margin", MARGIN_RIGHT, c->get_margin(MARGIN_RIGHT)); - undo_redo->add_undo_method(c, "set_margin", MARGIN_BOTTOM, c->get_margin(MARGIN_BOTTOM)); } undo_redo->commit_action(); @@ -3326,13 +3299,11 @@ void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { undo_redo->create_action(TTR("Change Anchors")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - Control *c = Object::cast_to<Control>(E->get()); - - undo_redo->add_do_method(c, "set_anchors_preset", p_preset); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_LEFT, c->get_anchor(MARGIN_LEFT)); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_TOP, c->get_anchor(MARGIN_TOP)); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_RIGHT, c->get_anchor(MARGIN_RIGHT)); - undo_redo->add_undo_method(c, "set_anchor", MARGIN_BOTTOM, c->get_anchor(MARGIN_BOTTOM)); + Control *control = Object::cast_to<Control>(E->get()); + if (control) { + undo_redo->add_do_method(control, "set_anchors_preset", p_preset); + undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); + } } undo_redo->commit_action(); @@ -3346,30 +3317,39 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { zoom = p_zoom; Point2 ofs = p_position; ofs = ofs / prev_zoom - ofs / zoom; - h_scroll->set_value(Math::round(h_scroll->get_value() + ofs.x)); - v_scroll->set_value(Math::round(v_scroll->get_value() + ofs.y)); + view_offset.x = Math::round(view_offset.x + ofs.x); + view_offset.y = Math::round(view_offset.y + ofs.y); - _update_scroll(0); + _update_scrollbars(); viewport->update(); - viewport_base->update(); } -void CanvasItemEditor::_zoom_minus() { - _zoom_on_position(zoom / 2.0, viewport_scrollable->get_size() / 2.0); +void CanvasItemEditor::_button_zoom_minus() { + _zoom_on_position(zoom / 1.5, viewport_scrollable->get_size() / 2.0); } -void CanvasItemEditor::_zoom_reset() { +void CanvasItemEditor::_button_zoom_reset() { _zoom_on_position(1.0, viewport_scrollable->get_size() / 2.0); } -void CanvasItemEditor::_zoom_plus() { - _zoom_on_position(zoom * 2.0, viewport_scrollable->get_size() / 2.0); +void CanvasItemEditor::_button_zoom_plus() { + _zoom_on_position(zoom * 1.5, viewport_scrollable->get_size() / 2.0); } -void CanvasItemEditor::_toggle_snap(bool p_status) { +void CanvasItemEditor::_button_toggle_snap(bool p_status) { snap_active = p_status; viewport->update(); - viewport_base->update(); +} + +void CanvasItemEditor::_button_tool_select(int p_index) { + + ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, rotate_button, pivot_button, pan_button }; + for (int i = 0; i < TOOL_MAX; i++) { + tb[i]->set_pressed(i == p_index); + } + + viewport->update(); + tool = (Tool)p_index; } void CanvasItemEditor::_popup_callback(int p_op) { @@ -3382,7 +3362,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { int idx = view_menu->get_popup()->get_item_index(SHOW_GRID); view_menu->get_popup()->set_item_checked(idx, show_grid); viewport->update(); - viewport_base->update(); + } break; + case SHOW_ORIGIN: { + show_origin = !show_origin; + int idx = view_menu->get_popup()->get_item_index(SHOW_ORIGIN); + view_menu->get_popup()->set_item_checked(idx, show_origin); + viewport->update(); + } break; + case SHOW_VIEWPORT: { + show_viewport = !show_viewport; + int idx = view_menu->get_popup()->get_item_index(SHOW_VIEWPORT); + view_menu->get_popup()->set_item_checked(idx, show_viewport); + viewport->update(); } break; case SNAP_USE_NODE_PARENT: { snap_node_parent = !snap_node_parent; @@ -3399,6 +3390,11 @@ void CanvasItemEditor::_popup_callback(int p_op) { int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES); smartsnap_config_popup->set_item_checked(idx, snap_node_sides); } break; + case SNAP_USE_NODE_CENTER: { + snap_node_center = !snap_node_center; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_CENTER); + smartsnap_config_popup->set_item_checked(idx, snap_node_center); + } break; case SNAP_USE_OTHER_NODES: { snap_other_nodes = !snap_other_nodes; int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES); @@ -3424,7 +3420,6 @@ void CanvasItemEditor::_popup_callback(int p_op) { int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE); snap_config_menu->get_popup()->set_item_checked(idx, snap_relative); viewport->update(); - viewport_base->update(); } break; case SNAP_USE_PIXEL: { snap_pixel = !snap_pixel; @@ -3452,26 +3447,19 @@ void CanvasItemEditor::_popup_callback(int p_op) { int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); viewport->update(); - viewport_base->update(); } break; case SHOW_GUIDES: { show_guides = !show_guides; int idx = view_menu->get_popup()->get_item_index(SHOW_GUIDES); view_menu->get_popup()->set_item_checked(idx, show_guides); viewport->update(); - viewport_base->update(); } break; - case 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()); if (!canvas_item || !canvas_item->is_inside_tree()) continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; @@ -3481,35 +3469,25 @@ void CanvasItemEditor::_popup_callback(int p_op) { viewport->update(); } break; case 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()); if (!canvas_item || !canvas_item->is_inside_tree()) continue; - 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"); } - viewport->update(); - } break; case 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()); if (!canvas_item || !canvas_item->is_inside_tree()) continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; @@ -3519,26 +3497,19 @@ void CanvasItemEditor::_popup_callback(int p_op) { viewport->update(); } break; case 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()); if (!canvas_item || !canvas_item->is_inside_tree()) continue; - 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"); } - viewport->update(); - } break; - case ANCHORS_AND_MARGINS_PRESET_TOP_LEFT: { _set_anchors_and_margins_preset(PRESET_TOP_LEFT); } break; @@ -3723,28 +3694,6 @@ void CanvasItemEditor::_popup_callback(int p_op) { key_scale = key_scale_button->is_pressed(); } break; - /* - case ANIM_INSERT_POS_ROT - case ANIM_INSERT_POS_SCALE: - case ANIM_INSERT_ROT_SCALE: - case ANIM_INSERT_POS_ROT_SCALE: { - static const bool key_toggles[7][3]={ - {true,false,false}, - {false,true,false}, - {false,false,true}, - {true,true,false}, - {true,false,true}, - {false,true,true}, - {true,true,true} - }; - key_pos=key_toggles[p_op-ANIM_INSERT_POS][0]; - key_rot=key_toggles[p_op-ANIM_INSERT_POS][1]; - key_scale=key_toggles[p_op-ANIM_INSERT_POS][2]; - for(int i=ANIM_INSERT_POS;i<=ANIM_INSERT_POS_ROT_SCALE;i++) { - int idx = animation_menu->get_popup()->get_item_index(i); - animation_menu->get_popup()->set_item_checked(idx,i==p_op); - } - } break;*/ case ANIM_COPY_POSE: { pose_clipboard.clear(); @@ -3823,9 +3772,9 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (key_pos) ctrl->set_position(Point2()); /* - if (key_scale) - AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size()); - */ + if (key_scale) + AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size()); + */ } } @@ -3935,7 +3884,12 @@ void CanvasItemEditor::_focus_selection(int p_op) { //if (!canvas_item->is_visible_in_tree()) continue; ++count; - Rect2 item_rect = canvas_item->_edit_get_rect(); + Rect2 item_rect; + if (canvas_item->_edit_use_rect()) { + item_rect = canvas_item->_edit_get_rect(); + } else { + item_rect = Rect2(); + } Vector2 pos = canvas_item->get_global_transform().get_origin(); Vector2 scale = canvas_item->get_global_transform().get_scale(); @@ -3956,8 +3910,10 @@ void CanvasItemEditor::_focus_selection(int p_op) { center = rect.position + rect.size / 2; Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center); - h_scroll->set_value(h_scroll->get_value() - offset.x / zoom); - v_scroll->set_value(v_scroll->get_value() - offset.y / zoom); + view_offset.x -= offset.x / zoom; + view_offset.y -= offset.y / zoom; + _update_scrollbars(); + viewport->update(); } else { // VIEW_FRAME_TO_SELECTION @@ -3966,7 +3922,7 @@ void CanvasItemEditor::_focus_selection(int p_op) { float scale_y = viewport->get_size().y / rect.size.y; zoom = scale_x < scale_y ? scale_x : scale_y; zoom *= 0.90; - _update_scroll(0); + viewport->update(); call_deferred("_popup_callback", VIEW_CENTER_TO_SELECTION); } } @@ -3974,21 +3930,23 @@ void CanvasItemEditor::_focus_selection(int p_op) { void CanvasItemEditor::_bind_methods() { - ClassDB::bind_method("_zoom_minus", &CanvasItemEditor::_zoom_minus); - ClassDB::bind_method("_zoom_reset", &CanvasItemEditor::_zoom_reset); - ClassDB::bind_method("_zoom_plus", &CanvasItemEditor::_zoom_plus); - ClassDB::bind_method("_toggle_snap", &CanvasItemEditor::_toggle_snap); + ClassDB::bind_method("_button_zoom_minus", &CanvasItemEditor::_button_zoom_minus); + 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("_update_scroll", &CanvasItemEditor::_update_scroll); + ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars); ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback); ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data); - ClassDB::bind_method("_tool_select", &CanvasItemEditor::_tool_select); + ClassDB::bind_method("_button_tool_select", &CanvasItemEditor::_button_tool_select); ClassDB::bind_method("_keying_changed", &CanvasItemEditor::_keying_changed); ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input); ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport); - ClassDB::bind_method("_draw_viewport_base", &CanvasItemEditor::_draw_viewport_base); ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport); - ClassDB::bind_method("_gui_input_viewport_base", &CanvasItemEditor::_gui_input_viewport_base); ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); + ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); + ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed); + ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state); @@ -3997,11 +3955,185 @@ void CanvasItemEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("item_group_status_changed")); } +Dictionary CanvasItemEditor::get_state() const { + + Dictionary state; + state["zoom"] = zoom; + state["ofs"] = view_offset; + state["grid_offset"] = grid_offset; + state["grid_step"] = grid_step; + state["snap_rotation_offset"] = snap_rotation_offset; + state["snap_rotation_step"] = snap_rotation_step; + state["snap_active"] = snap_active; + state["snap_node_parent"] = snap_node_parent; + state["snap_node_anchors"] = snap_node_anchors; + state["snap_node_sides"] = snap_node_sides; + state["snap_node_center"] = snap_node_center; + state["snap_other_nodes"] = snap_other_nodes; + state["snap_grid"] = snap_grid; + state["snap_guides"] = snap_guides; + state["show_grid"] = show_grid; + state["show_origin"] = show_origin; + state["show_viewport"] = show_viewport; + state["show_rulers"] = show_rulers; + state["show_guides"] = show_guides; + state["show_helpers"] = show_helpers; + state["snap_rotation"] = snap_rotation; + state["snap_relative"] = snap_relative; + state["snap_pixel"] = snap_pixel; + state["skeleton_show_bones"] = skeleton_show_bones; + return state; +} + +void CanvasItemEditor::set_state(const Dictionary &p_state) { + + Dictionary state = p_state; + if (state.has("zoom")) { + zoom = p_state["zoom"]; + } + + if (state.has("ofs")) { + view_offset = p_state["ofs"]; + previous_update_view_offset = view_offset; + _update_scrollbars(); + } + + if (state.has("grid_offset")) { + grid_offset = state["grid_offset"]; + } + + if (state.has("grid_step")) { + grid_step = state["grid_step"]; + } + + if (state.has("snap_rotation_step")) { + snap_rotation_step = state["snap_rotation_step"]; + } + + if (state.has("snap_rotation_offset")) { + snap_rotation_offset = state["snap_rotation_offset"]; + } + + if (state.has("snap_active")) { + snap_active = state["snap_active"]; + snap_button->set_pressed(snap_active); + } + + if (state.has("snap_node_parent")) { + snap_node_parent = state["snap_node_parent"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); + smartsnap_config_popup->set_item_checked(idx, snap_node_parent); + } + + if (state.has("snap_node_anchors")) { + snap_node_anchors = state["snap_node_anchors"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_ANCHORS); + smartsnap_config_popup->set_item_checked(idx, snap_node_anchors); + } + + if (state.has("snap_node_sides")) { + snap_node_sides = state["snap_node_sides"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES); + smartsnap_config_popup->set_item_checked(idx, snap_node_sides); + } + + if (state.has("snap_node_center")) { + snap_node_center = state["snap_node_center"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_CENTER); + smartsnap_config_popup->set_item_checked(idx, snap_node_center); + } + + if (state.has("snap_other_nodes")) { + snap_other_nodes = state["snap_other_nodes"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES); + smartsnap_config_popup->set_item_checked(idx, snap_other_nodes); + } + + if (state.has("snap_guides")) { + snap_guides = state["snap_guides"]; + int idx = smartsnap_config_popup->get_item_index(SNAP_USE_GUIDES); + smartsnap_config_popup->set_item_checked(idx, snap_guides); + } + + if (state.has("snap_grid")) { + snap_grid = state["snap_grid"]; + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID); + snap_config_menu->get_popup()->set_item_checked(idx, snap_grid); + } + + if (state.has("show_grid")) { + show_grid = state["show_grid"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_GRID); + view_menu->get_popup()->set_item_checked(idx, show_grid); + } + + if (state.has("show_origin")) { + show_origin = state["show_origin"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_ORIGIN); + view_menu->get_popup()->set_item_checked(idx, show_origin); + } + + if (state.has("show_viewport")) { + show_viewport = state["show_viewport"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_VIEWPORT); + view_menu->get_popup()->set_item_checked(idx, show_viewport); + } + + if (state.has("show_rulers")) { + 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); + } + + if (state.has("show_guides")) { + show_guides = state["show_guides"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_GUIDES); + view_menu->get_popup()->set_item_checked(idx, show_guides); + } + + if (state.has("show_helpers")) { + show_helpers = state["show_helpers"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_HELPERS); + view_menu->get_popup()->set_item_checked(idx, show_helpers); + } + + if (state.has("snap_rotation")) { + snap_rotation = state["snap_rotation"]; + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); + snap_config_menu->get_popup()->set_item_checked(idx, snap_rotation); + } + + if (state.has("snap_relative")) { + snap_relative = state["snap_relative"]; + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE); + snap_config_menu->get_popup()->set_item_checked(idx, snap_relative); + } + + if (state.has("snap_pixel")) { + snap_pixel = state["snap_pixel"]; + int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_PIXEL); + snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel); + } + + if (state.has("skeleton_show_bones")) { + skeleton_show_bones = state["skeleton_show_bones"]; + int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES); + skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); + } + + viewport->update(); +} + void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { hb->add_child(p_control); } +void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) { + + hb->remove_child(p_control); +} + HSplitContainer *CanvasItemEditor::get_palette_split() { return palette_split; @@ -4018,6 +4150,7 @@ void CanvasItemEditor::focus_selection() { CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { + bone_list_dirty = false; tool = TOOL_SELECT; undo_redo = p_editor->get_undo_redo(); editor = p_editor; @@ -4037,21 +4170,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { bottom_split->add_child(palette_split); palette_split->set_v_size_flags(SIZE_EXPAND_FILL); - viewport_base = memnew(Control); - palette_split->add_child(viewport_base); - viewport_base->set_clip_contents(true); - viewport_base->connect("draw", this, "_draw_viewport_base"); - viewport_base->connect("gui_input", this, "_gui_input_viewport_base"); - viewport_base->set_focus_mode(FOCUS_ALL); - viewport_base->set_v_size_flags(SIZE_EXPAND_FILL); - viewport_base->set_h_size_flags(SIZE_EXPAND_FILL); - viewport_scrollable = memnew(Control); - viewport_base->add_child(viewport_scrollable); + palette_split->add_child(viewport_scrollable); viewport_scrollable->set_mouse_filter(MOUSE_FILTER_PASS); - viewport_scrollable->set_draw_behind_parent(true); - viewport_scrollable->set_anchors_and_margins_preset(Control::PRESET_WIDE); - viewport_scrollable->set_begin(Point2(RULER_WIDTH, RULER_WIDTH)); + viewport_scrollable->set_clip_contents(true); + viewport_scrollable->set_v_size_flags(SIZE_EXPAND_FILL); + viewport_scrollable->set_h_size_flags(SIZE_EXPAND_FILL); + viewport_scrollable->connect("draw", this, "_update_scrollbars"); ViewportContainer *scene_tree = memnew(ViewportContainer); viewport_scrollable->add_child(scene_tree); @@ -4064,46 +4189,48 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { viewport->set_mouse_filter(MOUSE_FILTER_PASS); viewport->set_anchors_and_margins_preset(Control::PRESET_WIDE); viewport->set_clip_contents(true); + viewport->set_focus_mode(FOCUS_ALL); viewport->connect("draw", this, "_draw_viewport"); viewport->connect("gui_input", this, "_gui_input_viewport"); h_scroll = memnew(HScrollBar); viewport->add_child(h_scroll); - h_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED); + h_scroll->connect("value_changed", this, "_update_scroll"); h_scroll->hide(); v_scroll = memnew(VScrollBar); viewport->add_child(v_scroll); - v_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED); + v_scroll->connect("value_changed", this, "_update_scroll"); v_scroll->hide(); - HBoxContainer *zoom_hb = memnew(HBoxContainer); + zoom_hb = memnew(HBoxContainer); viewport->add_child(zoom_hb); zoom_hb->set_begin(Point2(5, 5)); zoom_minus = memnew(ToolButton); zoom_hb->add_child(zoom_minus); - zoom_minus->connect("pressed", this, "_zoom_minus"); + zoom_minus->connect("pressed", this, "_button_zoom_minus"); + zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom out"), KEY_MASK_CMD | KEY_MINUS)); zoom_minus->set_focus_mode(FOCUS_NONE); zoom_reset = memnew(ToolButton); zoom_hb->add_child(zoom_reset); - zoom_reset->connect("pressed", this, "_zoom_reset"); + zoom_reset->connect("pressed", this, "_button_zoom_reset"); + zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom reset"), KEY_MASK_CMD | KEY_0)); zoom_reset->set_focus_mode(FOCUS_NONE); zoom_plus = memnew(ToolButton); zoom_hb->add_child(zoom_plus); - zoom_plus->connect("pressed", this, "_zoom_plus"); + zoom_plus->connect("pressed", this, "_button_zoom_plus"); + zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom in"), KEY_MASK_CMD | KEY_PLUS)); zoom_plus->set_focus_mode(FOCUS_NONE); updating_scroll = false; - handle_len = 10; - first_update = true; select_button = memnew(ToolButton); hb->add_child(select_button); select_button->set_toggle_mode(true); - select_button->connect("pressed", this, "_tool_select", make_binds(TOOL_SELECT)); + select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECT)); select_button->set_pressed(true); select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q)); select_button->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate") + "\n" + TTR("Alt+Drag: Move") + "\n" + TTR("Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).") + "\n" + TTR("Alt+RMB: Depth list selection")); @@ -4111,14 +4238,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { move_button = memnew(ToolButton); hb->add_child(move_button); move_button->set_toggle_mode(true); - move_button->connect("pressed", this, "_tool_select", make_binds(TOOL_MOVE)); + move_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_MOVE)); move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W)); move_button->set_tooltip(TTR("Move Mode")); rotate_button = memnew(ToolButton); hb->add_child(rotate_button); rotate_button->set_toggle_mode(true); - rotate_button->connect("pressed", this, "_tool_select", make_binds(TOOL_ROTATE)); + rotate_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_ROTATE)); rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E)); rotate_button->set_tooltip(TTR("Rotate Mode")); @@ -4127,19 +4254,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { list_select_button = memnew(ToolButton); hb->add_child(list_select_button); list_select_button->set_toggle_mode(true); - list_select_button->connect("pressed", this, "_tool_select", make_binds(TOOL_LIST_SELECT)); + list_select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_LIST_SELECT)); list_select_button->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); pivot_button = memnew(ToolButton); hb->add_child(pivot_button); pivot_button->set_toggle_mode(true); - pivot_button->connect("pressed", this, "_tool_select", make_binds(TOOL_EDIT_PIVOT)); + pivot_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_EDIT_PIVOT)); pivot_button->set_tooltip(TTR("Click to change object's rotation pivot.")); pan_button = memnew(ToolButton); hb->add_child(pan_button); pan_button->set_toggle_mode(true); - pan_button->connect("pressed", this, "_tool_select", make_binds(TOOL_PAN)); + pan_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PAN)); pan_button->set_tooltip(TTR("Pan Mode")); hb->add_child(memnew(VSeparator)); @@ -4147,7 +4274,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_button = memnew(ToolButton); hb->add_child(snap_button); snap_button->set_toggle_mode(true); - snap_button->connect("toggled", this, "_toggle_snap"); + snap_button->connect("toggled", this, "_button_toggle_snap"); snap_button->set_tooltip(TTR("Toggles snapping")); snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_S)); @@ -4174,6 +4301,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { 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); @@ -4207,13 +4335,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p = skeleton_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES); - p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES); - p->add_separator(); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES); p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN); + p->add_separator(); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Custom Bone(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES); + p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Custom Bones")), SKELETON_CLEAR_BONES); p->connect("id_pressed", this, "_popup_callback"); hb->add_child(memnew(VSeparator)); @@ -4225,9 +4353,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p = view_menu->get_popup(); 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); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show guides"), KEY_Y), SHOW_GUIDES); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_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); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT); p->add_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); @@ -4305,9 +4435,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { selection_menu->connect("id_pressed", this, "_selection_result_pressed"); selection_menu->connect("popup_hide", this, "_selection_menu_hide"); - drag_pivot_shortcut = ED_SHORTCUT("canvas_item_editor/drag_pivot", TTR("Drag pivot from mouse position"), KEY_MASK_SHIFT | KEY_V); - set_pivot_shortcut = ED_SHORTCUT("canvas_item_editor/set_pivot", TTR("Set pivot at mouse position"), KEY_V); - multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY); divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE); @@ -4315,14 +4442,15 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_rot = true; key_scale = false; - edited_guide_pos = Point2(); - edited_guide_index = -1; - show_grid = false; + show_origin = true; + show_viewport = true; show_helpers = false; show_rulers = true; show_guides = true; zoom = 1; + view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH); + previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen grid_offset = Point2(); grid_step = Point2(10, 10); grid_step_multiplier = 0; @@ -4332,6 +4460,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_node_parent = true; snap_node_anchors = true; snap_node_sides = true; + snap_node_center = true; snap_other_nodes = true; snap_grid = true; snap_guides = true; @@ -4339,17 +4468,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_pixel = false; skeleton_show_bones = true; skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true); - box_selecting = false; - //zoom=0.5; singleton = this; set_process_unhandled_key_input(true); - can_move_pivot = false; - drag = DRAG_NONE; + + drag_type = DRAG_NONE; + drag_from = Vector2(); + drag_to = Vector2(); + dragged_guide_pos = Point2(); + dragged_guide_index = -1; + bone_last_frame = 0; - additive_selection = false; - // Update the menus checkboxes + // Update the menus' checkboxes call_deferred("set_state", get_state()); } @@ -4372,7 +4503,7 @@ 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_base->grab_focus(); + canvas_item_editor->viewport->grab_focus(); } else { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index ace87f9fe2..a1957b892e 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -50,9 +50,6 @@ class CanvasItemEditorSelectedItem : public Object { GDCLASS(CanvasItemEditorSelectedItem, Object); public: - Variant undo_state; - Vector2 undo_pivot; - Transform2D prev_xform; float prev_rot; Rect2 prev_rect; @@ -62,6 +59,11 @@ public: Transform2D pre_drag_xform; Rect2 pre_drag_rect; + List<float> pre_drag_bones_length; + List<Dictionary> pre_drag_bones_undo_state; + + Dictionary undo_state; + CanvasItemEditorSelectedItem() { prev_rot = 0; } }; @@ -86,6 +88,7 @@ class CanvasItemEditor : public VBoxContainer { SNAP_USE_NODE_PARENT, SNAP_USE_NODE_ANCHORS, SNAP_USE_NODE_SIDES, + SNAP_USE_NODE_CENTER, SNAP_USE_OTHER_NODES, SNAP_USE_GRID, SNAP_USE_GUIDES, @@ -97,6 +100,8 @@ class CanvasItemEditor : public VBoxContainer { SHOW_HELPERS, SHOW_RULERS, SHOW_GUIDES, + SHOW_ORIGIN, + SHOW_VIEWPORT, LOCK_SELECTED, UNLOCK_SELECTED, GROUP_SELECTED, @@ -169,6 +174,7 @@ class CanvasItemEditor : public VBoxContainer { enum DragType { DRAG_NONE, + DRAG_BOX_SELECTION, DRAG_LEFT, DRAG_TOP_LEFT, DRAG_TOP, @@ -185,29 +191,20 @@ class CanvasItemEditor : public VBoxContainer { DRAG_ALL, DRAG_ROTATE, DRAG_PIVOT, - DRAG_NODE_2D, DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - }; - - enum KeyMoveMODE { - MOVE_VIEW_BASE, - MOVE_LOCAL_BASE, - MOVE_LOCAL_WITH_ROT + DRAG_KEY_MOVE, + DRAG_PAN }; EditorSelection *editor_selection; - bool additive_selection; + bool selection_menu_additive_selection; Tool tool; - bool first_update; Control *viewport; - Control *viewport_base; Control *viewport_scrollable; - bool can_move_pivot; - HScrollBar *h_scroll; VScrollBar *v_scroll; HBoxContainer *hb; @@ -220,8 +217,12 @@ class CanvasItemEditor : public VBoxContainer { bool show_grid; bool show_rulers; bool show_guides; + bool show_origin; + bool show_viewport; bool show_helpers; float zoom; + Point2 view_offset; + Point2 previous_update_view_offset; Point2 grid_offset; Point2 grid_step; @@ -233,6 +234,7 @@ class CanvasItemEditor : public VBoxContainer { bool snap_node_parent; bool snap_node_anchors; bool snap_node_sides; + bool snap_node_center; bool snap_other_nodes; bool snap_grid; bool snap_guides; @@ -240,14 +242,10 @@ class CanvasItemEditor : public VBoxContainer { bool snap_relative; bool snap_pixel; bool skeleton_show_bones; - bool box_selecting; - Point2 box_selecting_to; bool key_pos; bool key_rot; bool key_scale; - void _tool_select(int p_index); - MenuOption last_option; struct _SelectResult { @@ -261,39 +259,36 @@ class CanvasItemEditor : public VBoxContainer { }; Vector<_SelectResult> selection_results; + Vector<_SelectResult> hovering_results; struct BoneList { Transform2D xform; - Vector2 from; - Vector2 to; - ObjectID bone; + float length; uint64_t last_pass; }; uint64_t bone_last_frame; - Map<ObjectID, BoneList> bone_list; - - Transform2D bone_orig_xform; - struct BoneIK { - - Variant orig_state; - Vector2 pos; - float len; - Node2D *node; + struct BoneKey { + ObjectID from; + ObjectID to; + _FORCE_INLINE_ bool operator<(const BoneKey &p_key) const { + if (from == p_key.from) + return to < p_key.to; + else + return from < p_key.from; + } }; - List<BoneIK> bone_ik_list; + Map<BoneKey, BoneList> bone_list; struct PoseClipboard { - Vector2 pos; Vector2 scale; float rot; ObjectID id; }; - List<PoseClipboard> pose_clipboard; ToolButton *select_button; @@ -333,16 +328,17 @@ class CanvasItemEditor : public VBoxContainer { Control *top_ruler; Control *left_ruler; - //PopupMenu *popup; - DragType drag; + DragType drag_type; Point2 drag_from; - Point2 drag_point_from; + Point2 drag_to; + Point2 drag_rotation_center; + List<CanvasItem *> drag_selection; + int dragged_guide_index; + Point2 dragged_guide_pos; + bool updating_value_dialog; - Point2 display_rotate_from; - Point2 display_rotate_to; - int edited_guide_index; - Point2 edited_guide_pos; + Point2 box_selecting_to; Ref<StyleBoxTexture> select_sb; Ref<Texture> select_handle; @@ -353,28 +349,21 @@ class CanvasItemEditor : public VBoxContainer { Ref<ShortCut> multiply_grid_step_shortcut; Ref<ShortCut> divide_grid_step_shortcut; - int handle_len; - bool _is_part_of_subscene(CanvasItem *p_item); - void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, Vector<_SelectResult> &r_items, int limit = 0); - void _find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, List<CanvasItem *> *r_items); + void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0); - void _select_click_on_empty_area(Point2 p_click_pos, bool p_append, bool p_box_selection); - bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag); + void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append); ConfirmationDialog *snap_dialog; CanvasItem *ref_item; - void _edit_set_pivot(const Vector2 &mouse_pos); void _add_canvas_item(CanvasItem *p_canvas_item); - void _remove_canvas_item(CanvasItem *p_canvas_item); - void _clear_canvas_items(); - void _key_move(const Vector2 &p_dir, bool p_snap, KeyMoveMODE p_move_mode); - void _list_select(const Ref<InputEventMouseButton> &b); - DragType _get_resize_handle_drag_type(const Point2 &p_click, Vector2 &r_point); - void _prepare_drag(const Point2 &p_click_pos); - DragType _get_anchor_handle_drag_type(const Point2 &p_click, Vector2 &r_point); + void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false); + void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false); + void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false); Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor); Vector2 _position_to_anchor(const Control *p_control, Vector2 position); @@ -383,27 +372,21 @@ class CanvasItemEditor : public VBoxContainer { bool updating_scroll; void _update_scroll(float); void _update_scrollbars(); - void _update_cursor(); - void incbeg(float &beg, float &end, float inc, float minsize, bool p_symmetric); - void incend(float &beg, float &end, float inc, float minsize, bool p_symmetric); - void _append_canvas_item(CanvasItem *p_item); void _snap_changed(); void _selection_result_pressed(int); void _selection_menu_hide(); UndoRedo *undo_redo; + bool _build_bones_list(Node *p_node); - Point2 _find_topleftmost_point(); - - void _build_bones_list(Node *p_node); - - void _get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform); + List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true); + Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); + void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + Rect2 _get_encompassing_rect(const Node *p_node); Object *_get_editor_data(Object *p_what); - CanvasItem *_get_single_item(); - int get_item_count(); void _keying_changed(); void _unhandled_key_input(const Ref<InputEvent> &p_ev); @@ -411,6 +394,7 @@ class CanvasItemEditor : public VBoxContainer { void _draw_text_at_position(Point2 p_position, String p_string, Margin p_side); void _draw_margin_at_position(int p_value, Point2 p_position, Margin p_side); void _draw_percentage_at_position(float p_value, Point2 p_position, Margin p_side); + void _draw_straight_line(Point2 p_from, Point2 p_to, Color p_color); void _draw_rulers(); void _draw_guides(); @@ -419,16 +403,29 @@ class CanvasItemEditor : public VBoxContainer { void _draw_selection(); void _draw_axis(); void _draw_bones(); - void _draw_locks_and_groups(Node *p_node, const Transform2D &p_xform); + void _draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _draw_hover(); void _draw_viewport(); - void _draw_viewport_base(); + + bool _gui_input_anchors(const Ref<InputEvent> &p_event); + bool _gui_input_move(const Ref<InputEvent> &p_event); + bool _gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event); + bool _gui_input_pivot(const Ref<InputEvent> &p_event); + bool _gui_input_resize(const Ref<InputEvent> &p_event); + bool _gui_input_rotate(const Ref<InputEvent> &p_event); + bool _gui_input_select(const Ref<InputEvent> &p_event); + bool _gui_input_zoom_or_pan(const Ref<InputEvent> &p_event); + bool _gui_input_rulers_and_guides(const Ref<InputEvent> &p_event); + bool _gui_input_hover(const Ref<InputEvent> &p_event); void _gui_input_viewport(const Ref<InputEvent> &p_event); - void _gui_input_viewport_base(const Ref<InputEvent> &p_event); void _focus_selection(int p_op); + void _solve_IK(Node2D *leaf_node, Point2 target_position); + void _snap_if_closer_float(float p_value, float p_target_snap, float &r_current_snap, bool &r_snapped, float p_radius = 10.0); void _snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation = 0.0, float p_radius = 10.0); void _snap_other_nodes(Point2 p_value, Point2 &r_current_snap, bool (&r_snapped)[2], const Node *p_current, const CanvasItem *p_to_snap = NULL); @@ -437,16 +434,22 @@ class CanvasItemEditor : public VBoxContainer { void _set_margins_preset(Control::LayoutPreset p_preset); void _set_anchors_and_margins_preset(Control::LayoutPreset p_preset); + HBoxContainer *zoom_hb; void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); - void _zoom_minus(); - void _zoom_reset(); - void _zoom_plus(); - - void _toggle_snap(bool p_status); + void _button_zoom_minus(); + void _button_zoom_reset(); + void _button_zoom_plus(); + void _button_toggle_snap(bool p_status); + void _button_tool_select(int p_index); HSplitContainer *palette_split; VSplitContainer *bottom_split; + bool bone_list_dirty; + void _queue_update_bone_list(); + void _update_bone_list(); + void _tree_changed(Node *); + friend class CanvasItemEditorPlugin; protected: @@ -494,9 +497,10 @@ public: SNAP_NODE_PARENT = 1 << 3, SNAP_NODE_ANCHORS = 1 << 4, SNAP_NODE_SIDES = 1 << 5, - SNAP_OTHER_NODES = 1 << 6, + SNAP_NODE_CENTER = 1 << 6, + SNAP_OTHER_NODES = 1 << 7, - SNAP_DEFAULT = 0x07, + SNAP_DEFAULT = SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL, }; Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, const CanvasItem *p_canvas_item = NULL, unsigned int p_forced_modes = 0); @@ -509,6 +513,7 @@ public: void set_state(const Dictionary &p_state); void add_control_to_menu_panel(Control *p_control); + void remove_control_from_menu_panel(Control *p_control); HSplitContainer *get_palette_split(); VSplitContainer *get_bottom_split(); diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp index d8d3c0cee6..e837359d0c 100644 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_editor_plugin.cpp @@ -33,10 +33,12 @@ #include "canvas_item_editor_plugin.h" #include "editor/editor_settings.h" #include "os/file_access.h" +#include "os/input.h" +#include "os/keyboard.h" #include "scene/3d/camera.h" #include "spatial_editor_plugin.h" -void CollisionPolygonEditor::_notification(int p_what) { +void Polygon3DEditor::_notification(int p_what) { switch (p_what) { @@ -53,15 +55,15 @@ void CollisionPolygonEditor::_notification(int p_what) { return; } - if (node->get_depth() != prev_depth) { + if (_get_depth() != prev_depth) { _polygon_draw(); - prev_depth = node->get_depth(); + prev_depth = _get_depth(); } } break; } } -void CollisionPolygonEditor::_node_removed(Node *p_node) { +void Polygon3DEditor::_node_removed(Node *p_node) { if (p_node == node) { node = NULL; @@ -72,7 +74,7 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) { } } -void CollisionPolygonEditor::_menu_option(int p_option) { +void Polygon3DEditor::_menu_option(int p_option) { switch (p_option) { @@ -91,10 +93,10 @@ void CollisionPolygonEditor::_menu_option(int p_option) { } } -void CollisionPolygonEditor::_wip_close() { +void Polygon3DEditor::_wip_close() { undo_redo->create_action(TTR("Create Poly3D")); - undo_redo->add_undo_method(node, "set_polygon", node->get_polygon()); + undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon")); undo_redo->add_do_method(node, "set_polygon", wip); undo_redo->add_do_method(this, "_polygon_draw"); undo_redo->add_undo_method(this, "_polygon_draw"); @@ -107,14 +109,14 @@ void CollisionPolygonEditor::_wip_close() { undo_redo->commit_action(); } -bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { +bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { if (!node) return false; Transform gt = node->get_global_transform(); Transform gi = gt.affine_inverse(); - float depth = node->get_depth() * 0.5; + float depth = _get_depth() * 0.5; Vector3 n = gt.basis.get_axis(2).normalized(); Plane p(gt.origin + n * depth, n); @@ -135,12 +137,14 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Vector2 cpoint(spoint.x, spoint.y); - cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + //DO NOT snap here, it's confusing in 3D for adding points. + //Let the snap happen when the point is being moved, instead. + //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); - Vector<Vector2> poly = node->get_polygon(); + Vector<Vector2> poly = node->call("get_polygon"); //first check if a point is to be added (segment split) - real_t grab_treshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); + real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); switch (mode) { @@ -154,12 +158,13 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R wip.push_back(cpoint); wip_active = true; edited_point_pos = cpoint; + snap_ignore = false; _polygon_draw(); edited_point = 1; return true; } else { - if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_treshold) { + if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) { //wip closed _wip_close(); @@ -168,6 +173,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R wip.push_back(cpoint); edited_point = wip.size(); + snap_ignore = false; _polygon_draw(); return true; } @@ -213,7 +219,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R continue; //not valid to reuse point real_t d = cp.distance_to(gpoint); - if (d < closest_dist && d < grab_treshold) { + if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; closest_idx = i; @@ -226,8 +232,10 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R poly.insert(closest_idx + 1, cpoint); edited_point = closest_idx + 1; edited_point_pos = cpoint; - node->set_polygon(poly); + node->call("set_polygon", poly); _polygon_draw(); + snap_ignore = true; + return true; } } else { @@ -242,7 +250,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); real_t d = cp.distance_to(gpoint); - if (d < closest_dist && d < grab_treshold) { + if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; closest_idx = i; @@ -255,11 +263,14 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R edited_point = closest_idx; edited_point_pos = poly[closest_idx]; _polygon_draw(); + snap_ignore = false; return true; } } } else { + snap_ignore = false; + if (edited_point != -1) { //apply @@ -288,7 +299,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))); real_t d = cp.distance_to(gpoint); - if (d < closest_dist && d < grab_treshold) { + if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; closest_idx = i; @@ -315,7 +326,6 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) { Vector2 gpoint = mm->get_position(); @@ -332,7 +342,13 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Vector2 cpoint(spoint.x, spoint.y); - cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + snap_ignore = false; + } + + if (!snap_ignore) { + cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + } edited_point_pos = cpoint; _polygon_draw(); @@ -341,7 +357,16 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R return false; } -void CollisionPolygonEditor::_polygon_draw() { + +float Polygon3DEditor::_get_depth() { + + if (bool(node->call("_has_editable_3d_polygon_no_depth"))) + return 0; + + return float(node->call("get_depth")); +} + +void Polygon3DEditor::_polygon_draw() { if (!node) return; @@ -351,9 +376,9 @@ void CollisionPolygonEditor::_polygon_draw() { if (wip_active) poly = wip; else - poly = node->get_polygon(); + poly = node->call("get_polygon"); - float depth = node->get_depth() * 0.5; + float depth = _get_depth() * 0.5; imgeom->clear(); imgeom->set_material_override(line_material); @@ -464,13 +489,13 @@ void CollisionPolygonEditor::_polygon_draw() { m->surface_set_material(0, handle_material); } -void CollisionPolygonEditor::edit(Node *p_collision_polygon) { +void Polygon3DEditor::edit(Node *p_collision_polygon) { if (p_collision_polygon) { - node = Object::cast_to<CollisionPolygon>(p_collision_polygon); + node = Object::cast_to<Spatial>(p_collision_polygon); //Enable the pencil tool if the polygon is empty - if (node->get_polygon().size() == 0) { + if (Vector<Vector2>(node->call("get_polygon")).size() == 0) { _menu_option(MODE_CREATE); } wip.clear(); @@ -491,14 +516,14 @@ void CollisionPolygonEditor::edit(Node *p_collision_polygon) { } } -void CollisionPolygonEditor::_bind_methods() { +void Polygon3DEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_menu_option"), &CollisionPolygonEditor::_menu_option); - ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygonEditor::_polygon_draw); - ClassDB::bind_method(D_METHOD("_node_removed"), &CollisionPolygonEditor::_node_removed); + ClassDB::bind_method(D_METHOD("_menu_option"), &Polygon3DEditor::_menu_option); + ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw); + ClassDB::bind_method(D_METHOD("_node_removed"), &Polygon3DEditor::_node_removed); } -CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { +Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) { node = NULL; editor = p_editor; @@ -543,24 +568,26 @@ CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { m.instance(); pointsm->set_mesh(m); pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001))); + + snap_ignore = false; } -CollisionPolygonEditor::~CollisionPolygonEditor() { +Polygon3DEditor::~Polygon3DEditor() { memdelete(imgeom); } -void CollisionPolygonEditorPlugin::edit(Object *p_object) { +void Polygon3DEditorPlugin::edit(Object *p_object) { collision_polygon_editor->edit(Object::cast_to<Node>(p_object)); } -bool CollisionPolygonEditorPlugin::handles(Object *p_object) const { +bool Polygon3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("CollisionPolygon"); + return Object::cast_to<Spatial>(p_object) && bool(p_object->call("_is_editable_3d_polygon")); } -void CollisionPolygonEditorPlugin::make_visible(bool p_visible) { +void Polygon3DEditorPlugin::make_visible(bool p_visible) { if (p_visible) { collision_polygon_editor->show(); @@ -571,14 +598,14 @@ void CollisionPolygonEditorPlugin::make_visible(bool p_visible) { } } -CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) { +Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) { editor = p_node; - collision_polygon_editor = memnew(CollisionPolygonEditor(p_node)); + collision_polygon_editor = memnew(Polygon3DEditor(p_node)); SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); collision_polygon_editor->hide(); } -CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin() { +Polygon3DEditorPlugin::~Polygon3DEditorPlugin() { } diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h index f1f215b092..4229808e2f 100644 --- a/editor/plugins/collision_polygon_editor_plugin.h +++ b/editor/plugins/collision_polygon_editor_plugin.h @@ -44,9 +44,9 @@ class CanvasItemEditor; -class CollisionPolygonEditor : public HBoxContainer { +class Polygon3DEditor : public HBoxContainer { - GDCLASS(CollisionPolygonEditor, HBoxContainer); + GDCLASS(Polygon3DEditor, HBoxContainer); UndoRedo *undo_redo; enum Mode { @@ -66,7 +66,7 @@ class CollisionPolygonEditor : public HBoxContainer { EditorNode *editor; Panel *panel; - CollisionPolygon *node; + Spatial *node; ImmediateGeometry *imgeom; MeshInstance *pointsm; Ref<ArrayMesh> m; @@ -78,6 +78,7 @@ class CollisionPolygonEditor : public HBoxContainer { Vector<Vector2> pre_move_edit; Vector<Vector2> wip; bool wip_active; + bool snap_ignore; float prev_depth; @@ -85,6 +86,8 @@ class CollisionPolygonEditor : public HBoxContainer { void _polygon_draw(); void _menu_option(int p_option); + float _get_depth(); + protected: void _notification(int p_what); void _node_removed(Node *p_node); @@ -93,28 +96,28 @@ protected: public: virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event); void edit(Node *p_collision_polygon); - CollisionPolygonEditor(EditorNode *p_editor); - ~CollisionPolygonEditor(); + Polygon3DEditor(EditorNode *p_editor); + ~Polygon3DEditor(); }; -class CollisionPolygonEditorPlugin : public EditorPlugin { +class Polygon3DEditorPlugin : public EditorPlugin { - GDCLASS(CollisionPolygonEditorPlugin, EditorPlugin); + GDCLASS(Polygon3DEditorPlugin, EditorPlugin); - CollisionPolygonEditor *collision_polygon_editor; + Polygon3DEditor *collision_polygon_editor; EditorNode *editor; public: virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); } - virtual String get_name() const { return "CollisionPolygon"; } + virtual String get_name() const { return "Polygon3DEditor"; } 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); - CollisionPolygonEditorPlugin(EditorNode *p_node); - ~CollisionPolygonEditorPlugin(); + Polygon3DEditorPlugin(EditorNode *p_node); + ~Polygon3DEditorPlugin(); }; #endif // COLLISION_POLYGON_EDITOR_PLUGIN_H diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 215964235e..8542296bde 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -30,6 +30,7 @@ #include "editor_preview_plugins.h" +#include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "io/file_access_memory.h" @@ -39,6 +40,38 @@ #include "scene/resources/material.h" #include "scene/resources/mesh.h" +void post_process_preview(Ref<Image> p_image) { + + if (p_image->get_format() != Image::FORMAT_RGBA8) + p_image->convert(Image::FORMAT_RGBA8); + + p_image->lock(); + + const int w = p_image->get_width(); + const int h = p_image->get_height(); + + const int r = MIN(w, h) / 32; + const int r2 = r * r; + Color transparent = Color(0, 0, 0, 0); + + for (int i = 0; i < r; i++) { + for (int j = 0; j < r; j++) { + int dx = i - r; + int dy = j - r; + if (dx * dx + dy * dy > r2) { + p_image->set_pixel(i, j, transparent); + p_image->set_pixel(w - 1 - i, j, transparent); + p_image->set_pixel(w - 1 - i, h - 1 - j, transparent); + p_image->set_pixel(i, h - 1 - j, transparent); + } else { + break; + } + } + } + + p_image->unlock(); +} + bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); @@ -90,6 +123,7 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) { } img->resize(width, height); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -162,6 +196,7 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) { } img->resize(width, height); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -203,6 +238,7 @@ Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_ Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); + post_process_preview(img); ptex->create_from_image(img, 0); return ptex; @@ -258,6 +294,7 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) { thumbnail_size *= EDSCALE; img->convert(Image::FORMAT_RGBA8); img->resize(thumbnail_size, thumbnail_size); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); ptex->create_from_image(img, 0); return ptex; @@ -426,19 +463,36 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { img->create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8); Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color"); - bg_color.a = 1.0; Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color"); Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color"); Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color"); + if (EditorSettings::get_singleton()->get("text_editor/theme/color_theme") == "Adaptive") { + Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); + + bg_color = tm->get_color("text_editor/highlighting/background_color", "Editor"); + keyword_color = tm->get_color("text_editor/highlighting/keyword_color", "Editor"); + text_color = tm->get_color("text_editor/highlighting/text_color", "Editor"); + symbol_color = tm->get_color("text_editor/highlighting/symbol_color", "Editor"); + } + img->lock(); + if (bg_color.a == 0) + bg_color = Color(0, 0, 0, 0); + bg_color.a = MAX(bg_color.a, 0.2); // some background + for (int i = 0; i < thumbnail_size; i++) { for (int j = 0; j < thumbnail_size; j++) { img->set_pixel(i, j, bg_color); } } + const int x0 = thumbnail_size / 8; + const int y0 = thumbnail_size / 8; + const int available_height = thumbnail_size - 2 * y0; + col = x0; + bool prev_is_text = false; bool in_keyword = false; for (int i = 0; i < code.length(); i++) { @@ -471,8 +525,8 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { Color ul = color; ul.a *= 0.5; - img->set_pixel(col, line * 2, bg_color.blend(ul)); - img->set_pixel(col, line * 2 + 1, color); + img->set_pixel(col, y0 + line * 2, bg_color.blend(ul)); + img->set_pixel(col, y0 + line * 2 + 1, color); prev_is_text = _is_text_char(c); } @@ -482,9 +536,9 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { in_keyword = false; if (c == '\n') { - col = 0; + col = x0; line++; - if (line >= thumbnail_size / 2) + if (line >= available_height / 2) break; } else if (c == '\t') { col += 3; @@ -495,6 +549,8 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { img->unlock(); + post_process_preview(img); + Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); ptex->create_from_image(img, 0); @@ -762,6 +818,7 @@ Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) { } imgdata = PoolVector<uint8_t>::Write(); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture)); ptex->create_from_image(Image(w,h,0,Image::FORMAT_RGB8,img),0); @@ -831,6 +888,7 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) { thumbnail_size *= EDSCALE; img->convert(Image::FORMAT_RGBA8); img->resize(thumbnail_size, thumbnail_size); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); ptex->create_from_image(img, 0); diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 2e12515e30..35b5c3a5f0 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -33,6 +33,8 @@ #include "editor/editor_resource_preview.h" +void post_process_preview(Ref<Image> p_image); + class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index fd5a1f185f..8b44f672b0 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -42,9 +42,18 @@ bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) { set_item_text(idx, p_value); else if (what == "icon") set_item_icon(idx, p_value); - else if (what == "checkable") - set_item_checkable(idx, p_value); - else if (what == "checked") + else if (what == "checkable") { + // This keeps compatibility to/from versions where this property was a boolean, before radio buttons + switch ((int)p_value) { + case 0: + case 1: + set_item_checkable(idx, p_value); + break; + case 2: + set_item_radio_checkable(idx, true); + break; + } + } else if (what == "checked") set_item_checked(idx, p_value); else if (what == "id") set_item_id(idx, p_value); @@ -68,9 +77,14 @@ bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_text(idx); else if (what == "icon") r_ret = get_item_icon(idx); - else if (what == "checkable") - r_ret = is_item_checkable(idx); - else if (what == "checked") + else if (what == "checkable") { + // This keeps compatibility to/from versions where this property was a boolean, before radio buttons + if (!is_item_checkable(idx)) { + r_ret = 0; + } else { + r_ret = is_item_radio_checkable(idx) ? 2 : 1; + } + } else if (what == "checked") r_ret = is_item_checked(idx); else if (what == "id") r_ret = get_item_id(idx); @@ -95,7 +109,7 @@ void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const { int flags = get_flags(); if (flags & FLAG_CHECKABLE) { - p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable")); + p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button")); p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked")); } @@ -247,7 +261,7 @@ void ItemListEditor::_node_removed(Node *p_node) { void ItemListEditor::_notification(int p_notification) { - if (p_notification == NOTIFICATION_ENTER_TREE) { + if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) { add_button->set_icon(get_icon("Add", "EditorIcons")); del_button->set_icon(get_icon("Remove", "EditorIcons")); diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index bd7d3db10d..d6a071b9b9 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -74,7 +74,9 @@ public: virtual Ref<Texture> get_item_icon(int p_idx) const { return Ref<Texture>(); }; virtual void set_item_checkable(int p_idx, bool p_check) {} + virtual void set_item_radio_checkable(int p_idx, bool p_check) {} virtual bool is_item_checkable(int p_idx) const { return false; }; + virtual bool is_item_radio_checkable(int p_idx) const { return false; }; virtual void set_item_checked(int p_idx, bool p_checked) {} virtual bool is_item_checked(int p_idx) const { return false; }; @@ -145,7 +147,9 @@ public: virtual Ref<Texture> get_item_icon(int p_idx) const { return pp->get_item_icon(p_idx); } virtual void set_item_checkable(int p_idx, bool p_check) { pp->set_item_as_checkable(p_idx, p_check); } + virtual void set_item_radio_checkable(int p_idx, bool p_check) { pp->set_item_as_radio_checkable(p_idx, p_check); } virtual bool is_item_checkable(int p_idx) const { return pp->is_item_checkable(p_idx); } + virtual bool is_item_radio_checkable(int p_idx) const { return pp->is_item_radio_checkable(p_idx); } virtual void set_item_checked(int p_idx, bool p_checked) { pp->set_item_checked(p_idx, p_checked); } virtual bool is_item_checked(int p_idx) const { return pp->is_item_checked(p_idx); } diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index cb5f7ba76c..74b6f14c2e 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -344,6 +344,10 @@ void MeshInstanceEditor::_create_outline_mesh() { err_dialog->set_text(TTR("Mesh has not surface to create outlines from!")); err_dialog->popup_centered_minsize(); return; + } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) { + err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!")); + err_dialog->popup_centered_minsize(); + return; } Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value()); @@ -396,7 +400,7 @@ MeshInstanceEditor::MeshInstanceEditor() { options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Outline Mesh.."), MENU_OPTION_CREATE_OUTLINE_MESH); + options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1); options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2); diff --git a/editor/plugins/navigation_mesh_generator.cpp b/editor/plugins/navigation_mesh_generator.cpp index 5d4e5520f4..0537c5c31f 100644 --- a/editor/plugins/navigation_mesh_generator.cpp +++ b/editor/plugins/navigation_mesh_generator.cpp @@ -280,26 +280,20 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) _build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices); - if (hf) { - rcFreeHeightField(hf); - hf = 0; - } - if (chf) { - rcFreeCompactHeightfield(chf); - chf = 0; - } - if (cset) { - rcFreeContourSet(cset); - cset = 0; - } - if (poly_mesh) { - rcFreePolyMesh(poly_mesh); - poly_mesh = 0; - } - if (detail_mesh) { - rcFreePolyMeshDetail(detail_mesh); - detail_mesh = 0; - } + rcFreeHeightField(hf); + hf = 0; + + rcFreeCompactHeightfield(chf); + chf = 0; + + rcFreeContourSet(cset); + cset = 0; + + rcFreePolyMesh(poly_mesh); + poly_mesh = 0; + + rcFreePolyMeshDetail(detail_mesh); + detail_mesh = 0; } ep.step(TTR("Done!"), 11); } diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index abee9a9893..6d11079759 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -94,7 +94,7 @@ void Particles2DEditorPlugin::_generate_visibility_rect() { while (running < time) { uint64_t ticks = OS::get_singleton()->get_ticks_usec(); - ep.step("Generating..", int(running), true); + ep.step("Generating...", int(running), true); OS::get_singleton()->delay_usec(1000); Rect2 capture = particles->capture_rect(); @@ -229,7 +229,7 @@ void Particles2DEditorPlugin::_generate_emission_mask() { valid_normals.resize(vpc); } - ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image..")); + ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image...")); ERR_FAIL_COND(valid_positions.size() == 0); PoolVector<uint8_t> texdata; diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index d129b6c5c3..7728995a99 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -129,7 +129,7 @@ void ParticlesEditor::_generate_aabb() { while (running < time) { uint64_t ticks = OS::get_singleton()->get_ticks_usec(); - ep.step("Generating..", int(running), true); + ep.step("Generating...", int(running), true); OS::get_singleton()->delay_usec(1000); AABB capture = node->capture_aabb(); diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index 056219a575..6dde639c54 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -167,18 +167,12 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p Vector3 ofs; - if (p_cancel) { - - return; - } - if (t == 0) { - if (p_cancel) { - c->set_point_in(p_idx, p_restore); return; } + ur->create_action(TTR("Set Curve In Position")); ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx)); ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore); @@ -186,10 +180,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p } else { if (p_cancel) { - c->set_point_out(idx, p_restore); + return; } + ur->create_action(TTR("Set Curve Out Position")); ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx)); ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore); diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp new file mode 100644 index 0000000000..42f1adcadf --- /dev/null +++ b/editor/plugins/physical_bone_plugin.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* physical_bone_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "physical_bone_plugin.h" +#include "editor/plugins/spatial_editor_plugin.h" +#include "scene/3d/physics_body.h" + +void PhysicalBoneEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_on_toggle_button_transform_joint", "is_pressed"), &PhysicalBoneEditor::_on_toggle_button_transform_joint); +} + +void PhysicalBoneEditor::_on_toggle_button_transform_joint(bool p_is_pressed) { + + _set_move_joint(); +} + +void PhysicalBoneEditor::_set_move_joint() { + if (selected) { + selected->_set_gizmo_move_joint(button_transform_joint->is_pressed()); + } +} + +PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) : + editor(p_editor), + selected(NULL) { + + spatial_editor_hb = memnew(HBoxContainer); + spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN); + SpatialEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb); + + spatial_editor_hb->add_child(memnew(VSeparator)); + + 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_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"); + + hide(); +} + +PhysicalBoneEditor::~PhysicalBoneEditor() { + // TODO the spatial_editor_hb should be removed from SpatialEditor, but in this moment it's not possible + for (int i = spatial_editor_hb->get_child_count() - 1; 0 <= i; --i) { + Node *n = spatial_editor_hb->get_child(i); + spatial_editor_hb->remove_child(n); + memdelete(n); + } + memdelete(spatial_editor_hb); +} + +void PhysicalBoneEditor::set_selected(PhysicalBone *p_pb) { + + button_transform_joint->set_pressed(false); + + _set_move_joint(); + selected = p_pb; + _set_move_joint(); +} + +void PhysicalBoneEditor::hide() { + spatial_editor_hb->hide(); +} + +void PhysicalBoneEditor::show() { + spatial_editor_hb->show(); +} + +PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) : + editor(p_editor), + selected(NULL) { + + physical_bone_editor = memnew(PhysicalBoneEditor(editor)); +} + +void PhysicalBonePlugin::make_visible(bool p_visible) { + if (p_visible) { + + physical_bone_editor->show(); + } else { + + physical_bone_editor->hide(); + physical_bone_editor->set_selected(NULL); + selected = NULL; + } +} + +void PhysicalBonePlugin::edit(Object *p_node) { + selected = static_cast<PhysicalBone *>(p_node); // Trust it + ERR_FAIL_COND(!selected); + + physical_bone_editor->set_selected(selected); +} diff --git a/editor/plugins/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h new file mode 100644 index 0000000000..9e7a50307a --- /dev/null +++ b/editor/plugins/physical_bone_plugin.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* physical_bone_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 PHYSICAL_BONE_PLUGIN_H +#define PHYSICAL_BONE_PLUGIN_H + +#include "editor/editor_node.h" + +class PhysicalBoneEditor : public Object { + GDCLASS(PhysicalBoneEditor, Object); + + EditorNode *editor; + HBoxContainer *spatial_editor_hb; + ToolButton *button_transform_joint; + + PhysicalBone *selected; + +protected: + static void _bind_methods(); + +private: + void _on_toggle_button_transform_joint(bool p_is_pressed); + void _set_move_joint(); + +public: + PhysicalBoneEditor(EditorNode *p_editor); + ~PhysicalBoneEditor(); + + void set_selected(PhysicalBone *p_pb); + + void hide(); + void show(); +}; + +class PhysicalBonePlugin : public EditorPlugin { + GDCLASS(PhysicalBonePlugin, EditorPlugin); + + EditorNode *editor; + PhysicalBone *selected; + PhysicalBoneEditor *physical_bone_editor; + +public: + virtual String get_name() const { return "PhysicalBone"; } + virtual bool handles(Object *p_object) const { return p_object->is_class("PhysicalBone"); } + virtual void make_visible(bool p_visible); + virtual void edit(Object *p_node); + + PhysicalBonePlugin(EditorNode *p_editor); +}; + +#endif diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 1db6621718..f04e0a801c 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -35,7 +35,7 @@ #include "os/file_access.h" #include "os/input.h" #include "os/keyboard.h" - +#include "scene/2d/skeleton_2d.h" Node2D *Polygon2DEditor::_get_node() const { return node; @@ -59,10 +59,15 @@ void Polygon2DEditor::_notification(int p_what) { button_uv->set_icon(get_icon("Uv", "EditorIcons")); + uv_button[UV_MODE_CREATE]->set_icon(get_icon("Add", "EditorIcons")); uv_button[UV_MODE_EDIT_POINT]->set_icon(get_icon("ToolSelect", "EditorIcons")); uv_button[UV_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons")); uv_button[UV_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons")); uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons")); + uv_button[UV_MODE_ADD_SPLIT]->set_icon(get_icon("AddSplit", "EditorIcons")); + uv_button[UV_MODE_REMOVE_SPLIT]->set_icon(get_icon("DeleteSplit", "EditorIcons")); + uv_button[UV_MODE_PAINT_WEIGHT]->set_icon(get_icon("PaintVertex", "EditorIcons")); + uv_button[UV_MODE_CLEAR_WEIGHT]->set_icon(get_icon("UnpaintVertex", "EditorIcons")); b_snap_grid->set_icon(get_icon("Grid", "EditorIcons")); b_snap_enable->set_icon(get_icon("SnapGrid", "EditorIcons")); @@ -75,6 +80,172 @@ void Polygon2DEditor::_notification(int p_what) { } } +void Polygon2DEditor::_sync_bones() { + + print_line("syncinc"); + if (!node->has_node(node->get_skeleton())) { + error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node")); + error->popup_centered_minsize(); + return; + } + + Node *sn = node->get_node(node->get_skeleton()); + Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(sn); + + if (!skeleton) { + error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node")); + error->popup_centered_minsize(); + return; + } + + Array prev_bones = node->call("_get_bones"); + node->clear_bones(); + + print_line("bones in skeleton: " + itos(skeleton->get_bone_count())); + + for (int i = 0; i < skeleton->get_bone_count(); i++) { + NodePath path = skeleton->get_path_to(skeleton->get_bone(i)); + PoolVector<float> weights; + int wc = node->get_polygon().size(); + + for (int j = 0; j < prev_bones.size(); j += 2) { + NodePath pvp = prev_bones[j]; + PoolVector<float> pv = prev_bones[j + 1]; + if (pvp == path && pv.size() == wc) { + weights = pv; + } + } + + if (weights.size() == 0) { //create them + weights.resize(node->get_polygon().size()); + PoolVector<float>::Write w = weights.write(); + for (int j = 0; j < wc; j++) { + w[j] = 0.0; + } + } + + node->add_bone(path, weights); + } + Array new_bones = node->call("_get_bones"); + + undo_redo->create_action(TTR("Sync bones")); + undo_redo->add_do_method(node, "_set_bones", new_bones); + undo_redo->add_undo_method(node, "_set_bones", prev_bones); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->add_do_method(this, "_update_bone_list"); + undo_redo->add_undo_method(this, "_update_bone_list"); + undo_redo->commit_action(); +} + +void Polygon2DEditor::_update_bone_list() { + + NodePath selected; + while (bone_scroll_vb->get_child_count()) { + CheckBox *cb = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(0)); + if (cb && cb->is_pressed()) { + selected = cb->get_meta("bone_path"); + } + memdelete(bone_scroll_vb->get_child(0)); + } + + Ref<ButtonGroup> bg; + bg.instance(); + for (int i = 0; i < node->get_bone_count(); i++) { + CheckBox *cb = memnew(CheckBox); + NodePath np = node->get_bone_path(i); + String name; + if (np.get_name_count()) { + name = np.get_name(np.get_name_count() - 1); + } + if (name == String()) { + name = "Bone " + itos(i); + } + cb->set_text(name); + cb->set_button_group(bg); + cb->set_meta("bone_path", np); + bone_scroll_vb->add_child(cb); + + if (np == selected) + cb->set_pressed(true); + + cb->connect("pressed", this, "_bone_paint_selected", varray(i)); + } + + uv_edit_draw->update(); +} + +void Polygon2DEditor::_bone_paint_selected(int p_index) { + uv_edit_draw->update(); +} + +void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { + + if (p_mode == 0) { //uv + uv_button[UV_MODE_CREATE]->hide(); + for (int i = UV_MODE_MOVE; i <= UV_MODE_SCALE; i++) { + uv_button[i]->show(); + } + uv_button[UV_MODE_ADD_SPLIT]->hide(); + uv_button[UV_MODE_REMOVE_SPLIT]->hide(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); + _uv_mode(UV_MODE_EDIT_POINT); + + bone_scroll_main_vb->hide(); + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + + } else if (p_mode == 1) { //poly + for (int i = 0; i <= UV_MODE_SCALE; i++) { + uv_button[i]->show(); + } + uv_button[UV_MODE_ADD_SPLIT]->hide(); + uv_button[UV_MODE_REMOVE_SPLIT]->hide(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); + _uv_mode(UV_MODE_EDIT_POINT); + + bone_scroll_main_vb->hide(); + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + + } else if (p_mode == 2) { //splits + for (int i = 0; i <= UV_MODE_SCALE; i++) { + uv_button[i]->hide(); + } + uv_button[UV_MODE_ADD_SPLIT]->show(); + uv_button[UV_MODE_REMOVE_SPLIT]->show(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); + _uv_mode(UV_MODE_ADD_SPLIT); + + bone_scroll_main_vb->hide(); + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + + } else if (p_mode == 3) { //bones´ + for (int i = 0; i <= UV_MODE_REMOVE_SPLIT; i++) { + uv_button[i]->hide(); + } + uv_button[UV_MODE_PAINT_WEIGHT]->show(); + uv_button[UV_MODE_CLEAR_WEIGHT]->show(); + _uv_mode(UV_MODE_PAINT_WEIGHT); + + bone_scroll_main_vb->show(); + bone_paint_strength->show(); + bone_paint_radius->show(); + bone_paint_radius_label->show(); + _update_bone_list(); + bone_paint_pos = Vector2(-100000, -100000); //send brush away when switching + } + + uv_edit_draw->update(); +} + void Polygon2DEditor::_menu_option(int p_option) { switch (p_option) { @@ -143,6 +314,9 @@ void Polygon2DEditor::_menu_option(int p_option) { undo_redo->commit_action(); } break; + case UVEDIT_GRID_SETTINGS: { + grid_settings->popup_centered_minsize(); + } break; default: { AbstractPolygon2DEditor::_menu_option(p_option); } break; @@ -180,6 +354,10 @@ void Polygon2DEditor::_set_snap_step_y(float p_val) { void Polygon2DEditor::_uv_mode(int p_mode) { + split_create = false; + uv_drag = false; + uv_create = false; + uv_mode = UVMode(p_mode); for (int i = 0; i < UV_MODE_MAX; i++) { uv_button[i]->set_pressed(p_mode == i); @@ -202,8 +380,60 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_drag_from = Vector2(mb->get_position().x, mb->get_position().y); uv_drag = true; - uv_prev = node->get_uv(); + points_prev = node->get_uv(); + + if (uv_edit_mode[0]->is_pressed()) { //edit uv + points_prev = node->get_uv(); + } else { //edit polygon + points_prev = node->get_polygon(); + } + uv_move_current = uv_mode; + if (uv_move_current == UV_MODE_CREATE) { + + if (!uv_create) { + points_prev.resize(0); + Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y)); + points_prev.push_back(tuv); + uv_create_to = tuv; + point_drag_index = 0; + uv_drag_from = tuv; + uv_drag = true; + uv_create = true; + uv_create_uv_prev = node->get_uv(); + uv_create_poly_prev = node->get_polygon(); + uv_create_bones_prev = node->call("_get_bones"); + splits_prev = node->get_splits(); + node->set_polygon(points_prev); + node->set_uv(points_prev); + + } else { + Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y)); + + if (points_prev.size() > 3 && tuv.distance_to(points_prev[0]) < 8) { + undo_redo->create_action(TTR("Create Polygon & UV")); + undo_redo->add_do_method(node, "set_uv", node->get_uv()); + undo_redo->add_undo_method(node, "set_uv", points_prev); + undo_redo->add_do_method(node, "set_polygon", node->get_polygon()); + undo_redo->add_undo_method(node, "set_polygon", points_prev); + undo_redo->add_do_method(node, "clear_bones"); + undo_redo->add_undo_method(node, "_set_bones", node->call("_get_bones")); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + uv_drag = false; + uv_create = false; + _uv_mode(UV_MODE_EDIT_POINT); + } else { + points_prev.push_back(tuv); + point_drag_index = points_prev.size() - 1; + uv_drag_from = tuv; + } + node->set_polygon(points_prev); + node->set_uv(points_prev); + } + } + if (uv_move_current == UV_MODE_EDIT_POINT) { if (mb->get_shift() && mb->get_command()) @@ -216,39 +446,219 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { if (uv_move_current == UV_MODE_EDIT_POINT) { - uv_drag_index = -1; - for (int i = 0; i < uv_prev.size(); i++) { + point_drag_index = -1; + for (int i = 0; i < points_prev.size(); i++) { - Vector2 tuv = mtx.xform(uv_prev[i]); + Vector2 tuv = mtx.xform(points_prev[i]); if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) { uv_drag_from = tuv; - uv_drag_index = i; + point_drag_index = i; } } - if (uv_drag_index == -1) { + if (point_drag_index == -1) { uv_drag = false; } } - } else if (uv_drag) { + + if (uv_move_current == UV_MODE_ADD_SPLIT) { + + int split_to_index = -1; + split_to_index = -1; + for (int i = 0; i < points_prev.size(); i++) { + + Vector2 tuv = mtx.xform(points_prev[i]); + if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) { + split_to_index = i; + } + } + + if (split_to_index == -1) { + split_create = false; + return; + } + + if (split_create) { + + split_create = false; + if (split_to_index < point_drag_index) { + SWAP(split_to_index, point_drag_index); + } + bool valid = true; + String split_error; + if (split_to_index == point_drag_index) { + split_error = TTR("Split point with itself."); + valid = false; + } + if (split_to_index + 1 == point_drag_index) { + //not a split,goes along the edge + split_error = TTR("Split can't form an existing edge."); + valid = false; + } + if (split_to_index == points_prev.size() - 1 && point_drag_index == 0) { + //not a split,goes along the edge + split_error = TTR("Split can't form an existing edge."); + valid = false; + } + + for (int i = 0; i < splits_prev.size(); i += 2) { + + if (splits_prev[i] == point_drag_index && splits_prev[i + 1] == split_to_index) { + //already exists + split_error = TTR("Split already exists."); + valid = false; + break; + } + + int a_state; //-1, outside split, 0 split point, +1, inside split + if (point_drag_index == splits_prev[i] || point_drag_index == splits_prev[i + 1]) { + a_state = 0; + } else if (point_drag_index < splits_prev[i] || point_drag_index > splits_prev[i + 1]) { + a_state = -1; + } else { + a_state = 1; + } + + int b_state; //-1, outside split, 0 split point, +1, inside split + if (split_to_index == splits_prev[i] || split_to_index == splits_prev[i + 1]) { + b_state = 0; + } else if (split_to_index < splits_prev[i] || split_to_index > splits_prev[i + 1]) { + b_state = -1; + } else { + b_state = 1; + } + + if (b_state * a_state < 0) { + //crossing + split_error = "Split crosses another split."; + valid = false; + break; + } + } + + if (valid) { + + splits_prev.push_back(point_drag_index); + splits_prev.push_back(split_to_index); + + undo_redo->create_action(TTR("Add Split")); + + undo_redo->add_do_method(node, "set_splits", splits_prev); + undo_redo->add_undo_method(node, "set_splits", node->get_splits()); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + } else { + error->set_text(TTR("Invalid Split: ") + split_error); + error->popup_centered_minsize(); + } + + } else { + point_drag_index = split_to_index; + split_create = true; + splits_prev = node->get_splits(); + uv_create_to = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y)); + } + } + + if (uv_move_current == UV_MODE_REMOVE_SPLIT) { + + for (int i = 0; i < splits_prev.size(); i += 2) { + if (splits_prev[i] < 0 || splits_prev[i] >= points_prev.size()) + continue; + if (splits_prev[i + 1] < 0 || splits_prev[i] >= points_prev.size()) + continue; + Vector2 e[2] = { mtx.xform(points_prev[splits_prev[i]]), mtx.xform(points_prev[splits_prev[i + 1]]) }; + Vector2 mp = Vector2(mb->get_position().x, mb->get_position().y); + Vector2 cp = Geometry::get_closest_point_to_segment_2d(mp, e); + if (cp.distance_to(mp) < 8) { + splits_prev.remove(i); + splits_prev.remove(i); + + undo_redo->create_action(TTR("Remove Split")); + + undo_redo->add_do_method(node, "set_splits", splits_prev); + undo_redo->add_undo_method(node, "set_splits", node->get_splits()); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + + break; + } + } + } + + if (uv_move_current == UV_MODE_PAINT_WEIGHT || uv_move_current == UV_MODE_CLEAR_WEIGHT) { + + int bone_selected = -1; + for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) { + CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i)); + if (c && c->is_pressed()) { + bone_selected = i; + break; + } + } + + if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == points_prev.size()) { + + prev_weights = node->get_bone_weights(bone_selected); + bone_painting = true; + bone_painting_bone = bone_selected; + } + } + + } else if (uv_drag && !uv_create) { undo_redo->create_action(TTR("Transform UV Map")); - undo_redo->add_do_method(node, "set_uv", node->get_uv()); - undo_redo->add_undo_method(node, "set_uv", uv_prev); + + if (uv_edit_mode[0]->is_pressed()) { //edit uv + undo_redo->add_do_method(node, "set_uv", node->get_uv()); + undo_redo->add_undo_method(node, "set_uv", points_prev); + } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + undo_redo->add_do_method(node, "set_polygon", node->get_polygon()); + undo_redo->add_undo_method(node, "set_polygon", points_prev); + } undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->commit_action(); uv_drag = false; + } else if (bone_painting) { + + undo_redo->create_action(TTR("Paint bone weights")); + undo_redo->add_do_method(node, "set_bone_weights", bone_painting_bone, node->get_bone_weights(bone_painting_bone)); + undo_redo->add_undo_method(node, "set_bone_weights", bone_painting_bone, prev_weights); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + bone_painting = false; } } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { - if (uv_drag) { + if (uv_create) { + + uv_drag = false; + uv_create = false; + node->set_uv(uv_create_uv_prev); + node->set_polygon(uv_create_poly_prev); + node->call("_set_bones", uv_create_bones_prev); + node->set_splits(splits_prev); + uv_edit_draw->update(); + } else if (uv_drag) { uv_drag = false; - node->set_uv(uv_prev); + if (uv_edit_mode[0]->is_pressed()) { //edit uv + node->set_uv(points_prev); + } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + node->set_polygon(points_prev); + } uv_edit_draw->update(); + } else if (split_create) { + split_create = false; + uv_edit_draw->update(); + } else if (bone_painting) { + node->set_bone_weights(bone_painting_bone, prev_weights); } } else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { @@ -277,48 +687,66 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { switch (uv_move_current) { + case UV_MODE_CREATE: { + if (uv_create) { + uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y)); + } + } break; case UV_MODE_EDIT_POINT: { - PoolVector<Vector2> uv_new = uv_prev; - uv_new.set(uv_drag_index, uv_new[uv_drag_index] + drag); - node->set_uv(uv_new); + PoolVector<Vector2> uv_new = points_prev; + uv_new.set(point_drag_index, uv_new[point_drag_index] + drag); + + if (uv_edit_mode[0]->is_pressed()) { //edit uv + node->set_uv(uv_new); + } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + node->set_polygon(uv_new); + } } break; case UV_MODE_MOVE: { - PoolVector<Vector2> uv_new = uv_prev; + PoolVector<Vector2> uv_new = points_prev; for (int i = 0; i < uv_new.size(); i++) uv_new.set(i, uv_new[i] + drag); - node->set_uv(uv_new); + if (uv_edit_mode[0]->is_pressed()) { //edit uv + node->set_uv(uv_new); + } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + node->set_polygon(uv_new); + } } break; case UV_MODE_ROTATE: { Vector2 center; - PoolVector<Vector2> uv_new = uv_prev; + PoolVector<Vector2> uv_new = points_prev; for (int i = 0; i < uv_new.size(); i++) - center += uv_prev[i]; + center += points_prev[i]; center /= uv_new.size(); float angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized()); for (int i = 0; i < uv_new.size(); i++) { - Vector2 rel = uv_prev[i] - center; + Vector2 rel = points_prev[i] - center; rel = rel.rotated(angle); uv_new.set(i, center + rel); } - node->set_uv(uv_new); + if (uv_edit_mode[0]->is_pressed()) { //edit uv + node->set_uv(uv_new); + } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + node->set_polygon(uv_new); + } } break; case UV_MODE_SCALE: { Vector2 center; - PoolVector<Vector2> uv_new = uv_prev; + PoolVector<Vector2> uv_new = points_prev; for (int i = 0; i < uv_new.size(); i++) - center += uv_prev[i]; + center += points_prev[i]; center /= uv_new.size(); float from_dist = uv_drag_from.distance_to(mtx.xform(center)); @@ -329,14 +757,51 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { float scale = to_dist / from_dist; for (int i = 0; i < uv_new.size(); i++) { - Vector2 rel = uv_prev[i] - center; + Vector2 rel = points_prev[i] - center; rel = rel * scale; uv_new.set(i, center + rel); } - node->set_uv(uv_new); + if (uv_edit_mode[0]->is_pressed()) { //edit uv + node->set_uv(uv_new); + } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon + node->set_polygon(uv_new); + } } break; } + + if (bone_painting) { + bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); + PoolVector<float> painted_weights = node->get_bone_weights(bone_painting_bone); + + { + int pc = painted_weights.size(); + float amount = bone_paint_strength->get_value(); + float radius = bone_paint_radius->get_value() * EDSCALE; + + if (uv_mode == UV_MODE_CLEAR_WEIGHT) { + amount = -amount; + } + + PoolVector<float>::Write w = painted_weights.write(); + PoolVector<float>::Read r = prev_weights.read(); + PoolVector<Vector2>::Read rv = points_prev.read(); + + for (int i = 0; i < pc; i++) { + if (mtx.xform(rv[i]).distance_to(bone_paint_pos) < radius) { + w[i] = CLAMP(r[i] + amount, 0, 1); + } + } + } + + node->set_bone_weights(bone_painting_bone, painted_weights); + } + uv_edit_draw->update(); + } else if (split_create) { + uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y)); + uv_edit_draw->update(); + } else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { + bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); uv_edit_draw->update(); } } @@ -372,6 +837,8 @@ void Polygon2DEditor::_uv_draw() { if (base_tex.is_null()) return; + String warning; + Transform2D mtx; mtx.elements[2] = -uv_draw_ofs; mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom)); @@ -407,7 +874,31 @@ void Polygon2DEditor::_uv_draw() { } } - PoolVector<Vector2> uvs = node->get_uv(); + PoolVector<Vector2> uvs; + if (uv_edit_mode[0]->is_pressed()) { //edit uv + uvs = node->get_uv(); + } else { //edit polygon + uvs = node->get_polygon(); + } + + PoolVector<float>::Read weight_r; + + if (uv_edit_mode[3]->is_pressed()) { + int bone_selected = -1; + for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) { + CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i)); + if (c && c->is_pressed()) { + bone_selected = i; + break; + } + } + + if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uvs.size()) { + + weight_r = node->get_bone_weights(bone_selected).read(); + } + } + Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); Rect2 rect(Point2(), mtx.basis_xform(base_tex->get_size())); @@ -416,11 +907,87 @@ void Polygon2DEditor::_uv_draw() { for (int i = 0; i < uvs.size(); i++) { int next = (i + 1) % uvs.size(); - uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(uvs[next]), Color(0.9, 0.5, 0.5), 2); - uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5); + Vector2 next_point = uvs[next]; + if (uv_create && i == uvs.size() - 1) { + next_point = uv_create_to; + } + uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), Color(0.9, 0.5, 0.5), 2); + if (weight_r.ptr()) { + + Vector2 draw_pos = mtx.xform(uvs[i]); + float weight = weight_r[i]; + uv_edit_draw->draw_rect(Rect2(draw_pos - Vector2(2, 2) * EDSCALE, Vector2(5, 5) * EDSCALE), Color(weight, weight, weight, 1.0)); + } else { + uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5); + } rect.expand_to(mtx.basis_xform(uvs[i])); } + if (split_create) { + Vector2 from = uvs[point_drag_index]; + Vector2 to = uv_create_to; + uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), Color(0.9, 0.5, 0.5), 2); + } + + PoolVector<int> splits = node->get_splits(); + + for (int i = 0; i < splits.size(); i += 2) { + int idx_from = splits[i]; + int idx_to = splits[i + 1]; + if (idx_from < 0 || idx_to >= uvs.size()) + continue; + uv_edit_draw->draw_line(mtx.xform(uvs[idx_from]), mtx.xform(uvs[idx_to]), Color(0.9, 0.5, 0.5), 2); + } + + if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { + + NodePath bone_path; + for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) { + CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i)); + if (c && c->is_pressed()) { + bone_path = node->get_bone_path(i); + break; + } + } + + //draw skeleton + NodePath skeleton_path = node->get_skeleton(); + if (node->has_node(skeleton_path)) { + Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node(skeleton_path)); + if (skeleton) { + for (int i = 0; i < skeleton->get_bone_count(); i++) { + + Bone2D *bone = skeleton->get_bone(i); + if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) + continue; //not set + + bool current = bone_path == skeleton->get_path_to(bone); + + for (int j = 0; j < bone->get_child_count(); j++) { + + Node2D *n = Object::cast_to<Node2D>(bone->get_child(j)); + if (!n) + continue; + + bool edit_bone = n->has_meta("_edit_bone_") && n->get_meta("_edit_bone_"); + if (edit_bone) { + + Transform2D bone_xform = node->get_global_transform().affine_inverse() * (skeleton->get_global_transform() * bone->get_skeleton_rest()); + Transform2D endpoint_xform = bone_xform * n->get_transform(); + + Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), current ? 5 : 4); + uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, current ? 3 : 2); + } + } + } + } + } + + //draw paint circle + uv_edit_draw->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1)); + } + rect = rect.grow(200); updating_uv_scroll = true; uv_hscroll->set_min(rect.position.x); @@ -449,6 +1016,11 @@ void Polygon2DEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_snap_off_y"), &Polygon2DEditor::_set_snap_off_y); ClassDB::bind_method(D_METHOD("_set_snap_step_x"), &Polygon2DEditor::_set_snap_step_x); ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &Polygon2DEditor::_set_snap_step_y); + ClassDB::bind_method(D_METHOD("_uv_edit_mode_select"), &Polygon2DEditor::_uv_edit_mode_select); + 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("_bone_paint_selected"), &Polygon2DEditor::_bone_paint_selected); } Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const { @@ -481,6 +1053,40 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_edit->add_child(uv_main_vb); //uv_edit->set_child_rect(uv_main_vb); HBoxContainer *uv_mode_hb = memnew(HBoxContainer); + + uv_edit_group.instance(); + + uv_edit_mode[0] = memnew(ToolButton); + uv_mode_hb->add_child(uv_edit_mode[0]); + uv_edit_mode[0]->set_toggle_mode(true); + uv_edit_mode[1] = memnew(ToolButton); + uv_mode_hb->add_child(uv_edit_mode[1]); + uv_edit_mode[1]->set_toggle_mode(true); + uv_edit_mode[2] = memnew(ToolButton); + uv_mode_hb->add_child(uv_edit_mode[2]); + uv_edit_mode[2]->set_toggle_mode(true); + uv_edit_mode[3] = memnew(ToolButton); + uv_mode_hb->add_child(uv_edit_mode[3]); + uv_edit_mode[3]->set_toggle_mode(true); + + uv_edit_mode[0]->set_text(TTR("UV")); + uv_edit_mode[0]->set_pressed(true); + uv_edit_mode[1]->set_text(TTR("Poly")); + uv_edit_mode[2]->set_text(TTR("Splits")); + uv_edit_mode[3]->set_text(TTR("Bones")); + + uv_edit_mode[0]->set_button_group(uv_edit_group); + uv_edit_mode[1]->set_button_group(uv_edit_group); + uv_edit_mode[2]->set_button_group(uv_edit_group); + uv_edit_mode[3]->set_button_group(uv_edit_group); + + uv_edit_mode[0]->connect("pressed", this, "_uv_edit_mode_select", varray(0)); + uv_edit_mode[1]->connect("pressed", this, "_uv_edit_mode_select", varray(1)); + uv_edit_mode[2]->connect("pressed", this, "_uv_edit_mode_select", varray(2)); + uv_edit_mode[3]->connect("pressed", this, "_uv_edit_mode_select", varray(3)); + + uv_mode_hb->add_child(memnew(VSeparator)); + uv_main_vb->add_child(uv_mode_hb); for (int i = 0; i < UV_MODE_MAX; i++) { @@ -491,12 +1097,45 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_button[i]->set_focus_mode(FOCUS_NONE); } - uv_button[0]->set_tooltip(TTR("Move Point") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale")); - uv_button[1]->set_tooltip(TTR("Move Polygon")); - uv_button[2]->set_tooltip(TTR("Rotate Polygon")); - uv_button[3]->set_tooltip(TTR("Scale Polygon")); + uv_button[0]->set_tooltip(TTR("Create Polygon")); + uv_button[1]->set_tooltip(TTR("Move Point") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale")); + uv_button[2]->set_tooltip(TTR("Move Polygon")); + uv_button[3]->set_tooltip(TTR("Rotate Polygon")); + uv_button[4]->set_tooltip(TTR("Scale Polygon")); + uv_button[5]->set_tooltip(TTR("Connect two points to make a split")); + uv_button[6]->set_tooltip(TTR("Select a split to erase it")); + uv_button[7]->set_tooltip(TTR("Paint weights with specified intensity")); + uv_button[8]->set_tooltip(TTR("UnPaint weights with specified intensity")); + + uv_button[0]->hide(); + uv_button[5]->hide(); + uv_button[6]->hide(); + uv_button[7]->hide(); + uv_button[8]->hide(); + uv_button[1]->set_pressed(true); + + bone_paint_strength = memnew(HSlider); + uv_mode_hb->add_child(bone_paint_strength); + bone_paint_strength->set_custom_minimum_size(Size2(75 * EDSCALE, 0)); + bone_paint_strength->set_v_size_flags(SIZE_SHRINK_CENTER); + bone_paint_strength->set_min(0); + bone_paint_strength->set_max(1); + bone_paint_strength->set_step(0.01); + bone_paint_strength->set_value(0.5); + + bone_paint_radius_label = memnew(Label(" " + TTR("Radius:") + " ")); + uv_mode_hb->add_child(bone_paint_radius_label); + bone_paint_radius = memnew(SpinBox); + uv_mode_hb->add_child(bone_paint_radius); + + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + bone_paint_radius->set_min(1); + bone_paint_radius->set_max(100); + bone_paint_radius->set_step(1); + bone_paint_radius->set_value(32); - uv_button[0]->set_pressed(true); HBoxContainer *uv_main_hb = memnew(HBoxContainer); uv_main_vb->add_child(uv_main_hb); uv_edit_draw = memnew(Control); @@ -510,6 +1149,8 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_menu->get_popup()->add_item(TTR("UV->Polygon"), UVEDIT_UV_TO_POLYGON); uv_menu->get_popup()->add_separator(); uv_menu->get_popup()->add_item(TTR("Clear UV"), UVEDIT_UV_CLEAR); + uv_menu->get_popup()->add_separator(); + uv_menu->get_popup()->add_item(TTR("Grid Settings"), UVEDIT_GRID_SETTINGS); uv_menu->get_popup()->connect("id_pressed", this, "_menu_option"); uv_mode_hb->add_child(memnew(VSeparator)); @@ -532,8 +1173,11 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : b_snap_grid->set_tooltip(TTR("Show Grid")); b_snap_grid->connect("toggled", this, "_set_show_grid"); - uv_mode_hb->add_child(memnew(VSeparator)); - uv_mode_hb->add_child(memnew(Label(TTR("Grid Offset:")))); + grid_settings = memnew(AcceptDialog); + grid_settings->set_title(TTR("Configure Grid:")); + add_child(grid_settings); + VBoxContainer *grid_settings_vb = memnew(VBoxContainer); + grid_settings->add_child(grid_settings_vb); SpinBox *sb_off_x = memnew(SpinBox); sb_off_x->set_min(-256); @@ -542,7 +1186,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : sb_off_x->set_value(snap_offset.x); sb_off_x->set_suffix("px"); sb_off_x->connect("value_changed", this, "_set_snap_off_x"); - uv_mode_hb->add_child(sb_off_x); + grid_settings_vb->add_margin_child(TTR("Grid Offset X:"), sb_off_x); SpinBox *sb_off_y = memnew(SpinBox); sb_off_y->set_min(-256); @@ -551,10 +1195,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : sb_off_y->set_value(snap_offset.y); sb_off_y->set_suffix("px"); sb_off_y->connect("value_changed", this, "_set_snap_off_y"); - uv_mode_hb->add_child(sb_off_y); - - uv_mode_hb->add_child(memnew(VSeparator)); - uv_mode_hb->add_child(memnew(Label(TTR("Grid Step:")))); + grid_settings_vb->add_margin_child(TTR("Grid Offset Y:"), sb_off_y); SpinBox *sb_step_x = memnew(SpinBox); sb_step_x->set_min(-256); @@ -563,7 +1204,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : sb_step_x->set_value(snap_step.x); sb_step_x->set_suffix("px"); sb_step_x->connect("value_changed", this, "_set_snap_step_x"); - uv_mode_hb->add_child(sb_step_x); + grid_settings_vb->add_margin_child(TTR("Grid Step X:"), sb_step_x); SpinBox *sb_step_y = memnew(SpinBox); sb_step_y->set_min(-256); @@ -572,7 +1213,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : sb_step_y->set_value(snap_step.y); sb_step_y->set_suffix("px"); sb_step_y->connect("value_changed", this, "_set_snap_step_y"); - uv_mode_hb->add_child(sb_step_y); + grid_settings_vb->add_margin_child(TTR("Grid Step Y:"), sb_step_y); uv_mode_hb->add_child(memnew(VSeparator)); uv_icon_zoom = memnew(TextureRect); @@ -582,8 +1223,10 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_zoom->set_max(4); uv_zoom->set_value(1); uv_zoom->set_step(0.01); + uv_zoom->set_v_size_flags(SIZE_SHRINK_CENTER); + uv_mode_hb->add_child(uv_zoom); - uv_zoom->set_custom_minimum_size(Size2(200, 0)); + uv_zoom->set_custom_minimum_size(Size2(80 * EDSCALE, 0)); uv_zoom_value = memnew(SpinBox); uv_zoom->share(uv_zoom_value); uv_zoom_value->set_custom_minimum_size(Size2(50, 0)); @@ -597,12 +1240,29 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_main_vb->add_child(uv_hscroll); uv_hscroll->connect("value_changed", this, "_uv_scroll_changed"); + bone_scroll_main_vb = memnew(VBoxContainer); + bone_scroll_main_vb->hide(); + sync_bones = memnew(Button(TTR("Sync Bones to Polygon"))); + bone_scroll_main_vb->add_child(sync_bones); + uv_main_hb->add_child(bone_scroll_main_vb); + bone_scroll = memnew(ScrollContainer); + bone_scroll->set_v_scroll(true); + bone_scroll->set_h_scroll(false); + bone_scroll_main_vb->add_child(bone_scroll); + bone_scroll->set_v_size_flags(SIZE_EXPAND_FILL); + bone_scroll_vb = memnew(VBoxContainer); + bone_scroll->add_child(bone_scroll_vb); + sync_bones->connect("pressed", this, "_sync_bones"); + uv_edit_draw->connect("draw", this, "_uv_draw"); uv_edit_draw->connect("gui_input", this, "_uv_input"); uv_draw_zoom = 1.0; - uv_drag_index = -1; + point_drag_index = -1; uv_drag = false; + uv_create = false; updating_uv_scroll = false; + split_create = false; + bone_painting = false; error = memnew(AcceptDialog); add_child(error); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index f1472e4522..f9b42a21c2 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -32,7 +32,7 @@ #define POLYGON_2D_EDITOR_PLUGIN_H #include "editor/plugins/abstract_polygon_2d_editor.h" - +#include "scene/gui/scroll_container.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -45,23 +45,32 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { MODE_EDIT_UV = MODE_CONT, UVEDIT_POLYGON_TO_UV, UVEDIT_UV_TO_POLYGON, - UVEDIT_UV_CLEAR + UVEDIT_UV_CLEAR, + UVEDIT_GRID_SETTINGS }; enum UVMode { + UV_MODE_CREATE, UV_MODE_EDIT_POINT, UV_MODE_MOVE, UV_MODE_ROTATE, UV_MODE_SCALE, + UV_MODE_ADD_SPLIT, + UV_MODE_REMOVE_SPLIT, + UV_MODE_PAINT_WEIGHT, + UV_MODE_CLEAR_WEIGHT, UV_MODE_MAX }; + ToolButton *uv_edit_mode[4]; + Ref<ButtonGroup> uv_edit_group; + Polygon2D *node; UVMode uv_mode; AcceptDialog *uv_edit; - ToolButton *uv_button[4]; + ToolButton *uv_button[UV_MODE_MAX]; ToolButton *b_snap_enable; ToolButton *b_snap_grid; Control *uv_edit_draw; @@ -72,11 +81,35 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { MenuButton *uv_menu; TextureRect *uv_icon_zoom; + VBoxContainer *bone_scroll_main_vb; + ScrollContainer *bone_scroll; + VBoxContainer *bone_scroll_vb; + Button *sync_bones; + HSlider *bone_paint_strength; + SpinBox *bone_paint_radius; + Label *bone_paint_radius_label; + bool bone_painting; + int bone_painting_bone; + PoolVector<float> prev_weights; + Vector2 bone_paint_pos; + AcceptDialog *grid_settings; + + void _sync_bones(); + void _update_bone_list(); + Vector2 uv_draw_ofs; float uv_draw_zoom; - PoolVector<Vector2> uv_prev; - int uv_drag_index; + PoolVector<Vector2> points_prev; + PoolVector<Vector2> uv_create_uv_prev; + PoolVector<Vector2> uv_create_poly_prev; + Array uv_create_bones_prev; + PoolVector<int> splits_prev; + + Vector2 uv_create_to; + int point_drag_index; bool uv_drag; + bool uv_create; + bool split_create; UVMode uv_move_current; Vector2 uv_drag_from; bool updating_uv_scroll; @@ -104,6 +137,9 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _set_snap_step_x(float p_val); void _set_snap_step_y(float p_val); + void _uv_edit_mode_select(int p_mode); + void _bone_paint_selected(int p_index); + protected: virtual Node2D *_get_node() const; virtual void _set_node(Node *p_polygon); diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index a47cb40240..c6e8ec1a2b 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -229,17 +229,34 @@ void ResourcePreloaderEditor::_update_library() { ERR_CONTINUE(r.is_null()); ti->set_tooltip(0, r->get_path()); - String type = r->get_class(); - ti->set_text(1, type); + ti->set_text(1, r->get_path()); + ti->add_button(1, get_icon("InstanceOptions", "EditorIcons"), BUTTON_SUBSCENE, false, TTR("Open in Editor")); + ti->set_tooltip(1, TTR("Instance:") + " " + r->get_path() + "\n" + TTR("Type:") + " " + r->get_class()); + ti->set_editable(1, false); ti->set_selectable(1, false); + String type = r->get_class(); + ti->set_text(2, type); + ti->set_selectable(2, false); if (has_icon(type, "EditorIcons")) - ti->set_icon(1, get_icon(type, "EditorIcons")); + ti->set_icon(2, get_icon(type, "EditorIcons")); } //player->add_resource("default",resource); } +void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) { + + TreeItem *item = Object::cast_to<TreeItem>(p_item); + ERR_FAIL_COND(!item); + + String rpath = item->get_text(p_column); + + if (p_id == BUTTON_SUBSCENE) { + EditorInterface::get_singleton()->open_scene_from_path(rpath); + } +} + void ResourcePreloaderEditor::edit(ResourcePreloader *p_preloader) { preloader = p_preloader; @@ -354,6 +371,7 @@ void ResourcePreloaderEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_delete_confirm_pressed"), &ResourcePreloaderEditor::_delete_confirm_pressed); ClassDB::bind_method(D_METHOD("_files_load_request"), &ResourcePreloaderEditor::_files_load_request); ClassDB::bind_method(D_METHOD("_update_library"), &ResourcePreloaderEditor::_update_library); + ClassDB::bind_method(D_METHOD("_cell_button_pressed"), &ResourcePreloaderEditor::_cell_button_pressed); ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &ResourcePreloaderEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &ResourcePreloaderEditor::can_drop_data_fw); @@ -385,11 +403,14 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { add_child(file); tree = memnew(Tree); - tree->set_columns(2); + tree->connect("button_pressed", this, "_cell_button_pressed"); + tree->set_columns(3); tree->set_column_min_width(0, 3); tree->set_column_min_width(1, 1); + tree->set_column_min_width(2, 1); tree->set_column_expand(0, true); tree->set_column_expand(1, true); + tree->set_column_expand(2, true); tree->set_v_size_flags(SIZE_EXPAND_FILL); tree->set_drag_forwarding(this); diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h index 6e04c70741..e737157785 100644 --- a/editor/plugins/resource_preloader_editor_plugin.h +++ b/editor/plugins/resource_preloader_editor_plugin.h @@ -42,6 +42,10 @@ class ResourcePreloaderEditor : public PanelContainer { GDCLASS(ResourcePreloaderEditor, PanelContainer); + enum { + BUTTON_SUBSCENE = 0, + }; + Button *load; Button *_delete; Button *paste; @@ -61,6 +65,7 @@ class ResourcePreloaderEditor : public PanelContainer { void _delete_pressed(); void _delete_confirm_pressed(); void _update_library(); + void _cell_button_pressed(Object *p_item, int p_column, int p_id); void _item_edited(); UndoRedo *undo_redo; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index d18422c0c0..fa674e1e34 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -39,9 +39,11 @@ #include "core/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/find_in_files.h" #include "editor/node_dock.h" #include "editor/script_editor_debugger.h" #include "scene/main/viewport.h" +#include "script_text_editor.h" /*** SCRIPT EDITOR ****/ @@ -54,6 +56,8 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("request_save_history")); ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what"))); + // TODO This signal is no use for VisualScript... + ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text"))); } static bool _can_open_in_editor(Script *p_script) { @@ -148,8 +152,6 @@ public: } }; -#define SORT_SCRIPT_LIST - void ScriptEditorQuickOpen::popup(const Vector<String> &p_functions, bool p_dontclear) { popup_centered_ratio(0.6); @@ -300,37 +302,34 @@ void ScriptEditor::_script_created(Ref<Script> p_script) { void ScriptEditor::_goto_script_line2(int p_line) { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); - if (!current) - return; - - current->goto_line(p_line); + ScriptEditorBase *current = _get_current_editor(); + if (current) + current->goto_line(p_line); } void ScriptEditor::_goto_script_line(REF p_script, int p_line) { Ref<Script> script = Object::cast_to<Script>(*p_script); - if (!script.is_null() && script->get_path().is_resource_file()) { + if (!script.is_null() && script->has_source_code()) { if (edit(p_script, p_line, 0)) { editor->push_item(p_script.ptr()); - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); - if (!current) - return; - - current->goto_line(p_line, true); + ScriptEditorBase *current = _get_current_editor(); + if (current) + current->goto_line(p_line, true); } } } +ScriptEditorBase *ScriptEditor::_get_current_editor() const { + + int selected = tab_container->get_current_tab(); + if (selected < 0 || selected >= tab_container->get_child_count()) + return NULL; + + return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); +} + void ScriptEditor::_update_history_arrows() { script_back->set_disabled(history_pos <= 0); @@ -429,36 +428,32 @@ void ScriptEditor::_add_recent_script(String p_path) { return; } - // remove if already stored - int already_recent = previous_scripts.find(p_path); - if (already_recent >= 0) { - previous_scripts.remove(already_recent); + Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array()); + if (rc.find(p_path) != -1) { + rc.erase(p_path); + } + rc.push_front(p_path); + if (rc.size() > 10) { + rc.resize(10); } - // add to list - previous_scripts.insert(0, p_path); - + EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc); _update_recent_scripts(); } void ScriptEditor::_update_recent_scripts() { - // make sure we don't exceed max size - const int max_history = EDITOR_DEF("text_editor/files/maximum_recent_files", 20); - if (previous_scripts.size() > max_history) { - previous_scripts.resize(max_history); - } - + Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array()); recent_scripts->clear(); recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/open_recent", TTR("Open Recent"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T)); recent_scripts->add_separator(); - const int max_shown = 8; - for (int i = 0; i < previous_scripts.size() && i <= max_shown; i++) { - String path = previous_scripts.get(i); - // just show script name and last dir - recent_scripts->add_item(path.get_slice("/", path.get_slice_count("/") - 2) + "/" + path.get_file()); + String path; + for (int i = 0; i < rc.size(); i++) { + + path = rc[i]; + recent_scripts->add_item(path.replace("res://", "")); } recent_scripts->add_separator(); @@ -471,7 +466,7 @@ void ScriptEditor::_open_recent_script(int p_idx) { // clear button if (p_idx == recent_scripts->get_item_count() - 1) { - previous_scripts.clear(); + EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", Array()); call_deferred("_update_recent_scripts"); return; } @@ -481,25 +476,37 @@ void ScriptEditor::_open_recent_script(int p_idx) { p_idx -= 2; } - if (p_idx < previous_scripts.size() && p_idx >= 0) { + Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array()); + ERR_FAIL_INDEX(p_idx, rc.size()); - String path = previous_scripts.get(p_idx); - // if its not on disk its a help file or deleted - if (FileAccess::exists(path)) { - Ref<Script> script = ResourceLoader::load(path); - if (script.is_valid()) { - edit(script, true); - } - // if it's a path then its most likely a delted file not help - } else if (!path.is_resource_file()) { - _help_class_open(path); + String path = rc[p_idx]; + // if its not on disk its a help file or deleted + if (FileAccess::exists(path)) { + Ref<Script> script = ResourceLoader::load(path); + if (script.is_valid()) { + edit(script, true); + return; } - previous_scripts.remove(p_idx); - _update_recent_scripts(); + + // if it's a path then its most likely a deleted file not help + } else if (!path.is_resource_file()) { + _help_class_open(path); + return; } + + rc.remove(p_idx); + EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc); + _update_recent_scripts(); + _show_error_dialog(path); +} + +void ScriptEditor::_show_error_dialog(String p_path) { + + error_dialog->set_text(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_path)); + error_dialog->popup_centered_minsize(); } -void ScriptEditor::_close_tab(int p_idx, bool p_save) { +void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { int selected = p_idx; if (selected < 0 || selected >= tab_container->get_child_count()) @@ -508,18 +515,16 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save) { Node *tselected = tab_container->get_child(selected); ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); if (current) { - _add_recent_script(current->get_edited_script()->get_path()); if (p_save) { apply_scripts(); } notify_script_close(current->get_edited_script()); - } else { - EditorHelp *help = Object::cast_to<EditorHelp>(tab_container->get_child(selected)); - _add_recent_script(help->get_class()); } // roll back to previous tab - _history_back(); + if (p_history_back) { + _history_back(); + } //remove from history history.resize(history_pos + 1); @@ -577,13 +582,13 @@ void ScriptEditor::_close_docs_tab() { EditorHelp *se = Object::cast_to<EditorHelp>(tab_container->get_child(i)); if (se) { - _close_tab(i); + _close_tab(i, true, false); } } } void ScriptEditor::_copy_script_path() { - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab())); + ScriptEditorBase *se = _get_current_editor(); Ref<Script> script = se->get_edited_script(); OS::get_singleton()->set_clipboard(script->get_path()); } @@ -815,11 +820,8 @@ void ScriptEditor::_file_dialog_action(String p_file) { Ref<Script> ScriptEditor::_get_current_script() { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return NULL; + ScriptEditorBase *current = _get_current_editor(); - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); if (current) { return current->get_edited_script(); } else { @@ -882,7 +884,7 @@ void ScriptEditor::_menu_option(int p_option) { file_dialog->add_filter("*.tet"); file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme"))); file_dialog->popup_centered_ratio(); - file_dialog->set_title(TTR("Save Theme As..")); + file_dialog->set_title(TTR("Save Theme As...")); } break; case SEARCH_HELP: { @@ -935,11 +937,7 @@ void ScriptEditor::_menu_option(int p_option) { } } - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); + ScriptEditorBase *current = _get_current_editor(); if (current) { switch (p_option) { @@ -1029,7 +1027,7 @@ void ScriptEditor::_menu_option(int p_option) { _copy_script_path(); } break; case SHOW_IN_FILE_SYSTEM: { - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab())); + ScriptEditorBase *se = _get_current_editor(); Ref<Script> script = se->get_edited_script(); FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock(); file_system_dock->navigate_to_path(script->get_path()); @@ -1219,6 +1217,17 @@ void ScriptEditor::_notification(int p_what) { recent_scripts->set_as_minsize(); } break; + case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { + + if (is_visible()) { + find_in_files_button->show(); + } else { + find_in_files->hide(); + find_in_files_button->hide(); + } + + } break; + default: break; } @@ -1226,15 +1235,11 @@ void ScriptEditor::_notification(int p_what) { bool ScriptEditor::can_take_away_focus() const { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return true; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); - if (!current) + ScriptEditorBase *current = _get_current_editor(); + if (current) + return current->can_lose_focus_on_node_selection(); + else return true; - - return current->can_lose_focus_on_node_selection(); } void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) { @@ -1311,28 +1316,22 @@ void ScriptEditor::ensure_focus_current() { if (!is_inside_tree()) return; - int cidx = tab_container->get_current_tab(); - if (cidx < 0 || cidx >= tab_container->get_tab_count()) - return; - - Control *c = Object::cast_to<Control>(tab_container->get_child(cidx)); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(c); - if (!se) - return; - se->ensure_focus(); + ScriptEditorBase *current = _get_current_editor(); + if (current) + current->ensure_focus(); } void ScriptEditor::_members_overview_selected(int p_idx) { - Node *current = tab_container->get_child(tab_container->get_current_tab()); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (!se) { return; } - // Go to the member's line and reset the cursor column. We can't just change scroll_position - // directly, since code might be folded. + // Go to the member's line and reset the cursor column. We can't change scroll_position + // directly until we have gone to the line first, since code might be folded. se->goto_line(members_overview->get_item_metadata(p_idx)); Dictionary state = se->get_edit_state(); state["column"] = 0; + state["scroll_position"] = members_overview->get_item_metadata(p_idx); se->set_edit_state(state); } @@ -1357,18 +1356,12 @@ void ScriptEditor::ensure_select_current() { if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) { - Node *current = tab_container->get_child(tab_container->get_current_tab()); - - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (se) { - Ref<Script> script = se->get_edited_script(); - if (!grab_focus_block && is_visible_in_tree()) se->ensure_focus(); } - - EditorHelp *eh = Object::cast_to<EditorHelp>(current); } _update_selected_editor_menu(); @@ -1408,12 +1401,7 @@ struct _ScriptEditorItemData { void ScriptEditor::_update_members_overview_visibility() { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - Node *current = tab_container->get_child(tab_container->get_current_tab()); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (!se) { members_overview->set_visible(false); return; @@ -1429,12 +1417,7 @@ void ScriptEditor::_update_members_overview_visibility() { void ScriptEditor::_update_members_overview() { members_overview->clear(); - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - Node *current = tab_container->get_child(tab_container->get_current_tab()); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (!se) { return; } @@ -1573,6 +1556,9 @@ void ScriptEditor::_update_script_names() { case SORT_BY_PATH: { sd.sort_key = path; } break; + case SORT_BY_NONE: { + sd.sort_key = ""; + } break; } switch (display_as) { @@ -1690,28 +1676,42 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path"); String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags"); - Dictionary keys; - keys["project"] = ProjectSettings::get_singleton()->get_resource_path(); - keys["file"] = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); - keys["line"] = p_line >= 0 ? p_line : 0; - keys["col"] = p_col; - - flags = flags.format(keys).strip_edges().replace("\\\\", "\\"); - List<String> args; if (flags.size()) { - int from = 0, to = 0; + String project_path = ProjectSettings::get_singleton()->get_resource_path(); + String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); + + flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0)); + flags = flags.replacen("{col}", itos(p_col)); + flags = flags.strip_edges().replace("\\\\", "\\"); + + int from = 0; + int num_chars = 0; bool inside_quotes = false; + for (int i = 0; i < flags.size(); i++) { + if (flags[i] == '"' && (!i || flags[i - 1] != '\\')) { + + if (!inside_quotes) { + from++; + } inside_quotes = !inside_quotes; + } else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) { - args.push_back(flags.substr(from, to)); + + String arg = flags.substr(from, num_chars); + + // do path replacement here, else there will be issues with spaces and quotes + arg = arg.replacen("{project}", project_path); + arg = arg.replacen("{file}", script_path); + args.push_back(arg); + from = i + 1; - to = 0; + num_chars = 0; } else { - to++; + num_chars++; } } } @@ -1756,6 +1756,20 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool } ERR_FAIL_COND_V(!se, false); + bool highlighter_set = false; + for (int i = 0; i < syntax_highlighters_func_count; i++) { + SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i](); + se->add_syntax_highlighter(highlighter); + + if (!highlighter_set) { + List<String> languages = highlighter->get_supported_languages(); + if (languages.find(p_script->get_language()->get_name())) { + se->set_syntax_highlighter(highlighter); + highlighter_set = true; + } + } + } + tab_container->add_child(se); se->set_edited_script(p_script); se->set_tooltip_request_func("_get_debug_tooltip", this); @@ -1777,6 +1791,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool se->connect("request_open_script_at_line", this, "_goto_script_line"); se->connect("go_to_help", this, "_help_class_goto"); se->connect("request_save_history", this, "_save_history"); + se->connect("search_in_files_requested", this, "_on_find_in_files_requested"); //test for modification, maybe the script was not edited but was loaded @@ -1787,6 +1802,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool se->goto_line(p_line - 1); notify_script_changed(p_script); + _add_recent_script(p_script->get_path()); return true; } @@ -2304,6 +2320,7 @@ void ScriptEditor::_help_class_open(const String &p_class) { _go_to_tab(tab_container->get_tab_count() - 1); eh->go_to_class(p_class, 0); eh->connect("go_to_help", this, "_help_class_goto"); + _add_recent_script(p_class); _update_script_names(); _save_layout(); } @@ -2332,6 +2349,7 @@ void ScriptEditor::_help_class_goto(const String &p_desc) { _go_to_tab(tab_container->get_tab_count() - 1); eh->go_to_help(p_desc); eh->connect("go_to_help", this, "_help_class_goto"); + _add_recent_script(eh->get_class()); _update_script_names(); _save_layout(); } @@ -2469,6 +2487,14 @@ void ScriptEditor::_open_script_request(const String &p_path) { } } +int ScriptEditor::syntax_highlighters_func_count = 0; +CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX]; + +void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) { + ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX); + syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func; +} + int ScriptEditor::script_editor_func_count = 0; CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX]; @@ -2483,6 +2509,48 @@ void ScriptEditor::_script_changed() { NodeDock::singleton->update_lists(); } +void ScriptEditor::_on_find_in_files_requested(String text) { + + find_in_files_dialog->set_search_text(text); + find_in_files_dialog->popup_centered_minsize(); +} + +void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { + + Ref<Resource> res = ResourceLoader::load(fpath); + edit(res); + + ScriptEditorBase *seb = _get_current_editor(); + + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(seb); + if (ste) { + ste->goto_line_selection(line_number - 1, begin, end); + } +} + +void ScriptEditor::_start_find_in_files(bool with_replace) { + + FindInFiles *f = find_in_files->get_finder(); + + f->set_search_text(find_in_files_dialog->get_search_text()); + f->set_match_case(find_in_files_dialog->is_match_case()); + f->set_whole_words(find_in_files_dialog->is_match_case()); + f->set_folder(find_in_files_dialog->get_folder()); + f->set_filter(find_in_files_dialog->get_filter()); + + find_in_files->set_with_replace(with_replace); + find_in_files->start_search(); + + find_in_files_button->set_pressed(true); + find_in_files->show(); +} + +void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) { + + _test_script_times_on_disk(); + _update_modified_scripts_for_external_editor(); +} + void ScriptEditor::_bind_methods() { ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action); @@ -2530,6 +2598,10 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input); ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed); ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts); + ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested); + ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files); + ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected); + ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files); ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw); @@ -2552,8 +2624,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { waiting_update_names = false; pending_auto_reload = false; auto_reload_running_scripts = false; - members_overview_enabled = true; - help_overview_enabled = true; + members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/open_scripts/show_members_overview"); + help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index"); editor = p_editor; VBoxContainer *main_container = memnew(VBoxContainer); @@ -2586,11 +2658,13 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { members_overview = memnew(ItemList); list_split->add_child(members_overview); + members_overview->set_allow_reselect(true); members_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing members_overview->set_v_size_flags(SIZE_EXPAND_FILL); help_overview = memnew(ItemList); list_split->add_child(help_overview); + help_overview->set_allow_reselect(true); help_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing help_overview->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2622,7 +2696,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT | KEY_MASK_CMD | KEY_S), FILE_SAVE); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As..")), FILE_SAVE_AS); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL); file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT); @@ -2651,7 +2725,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu = memnew(MenuButton); menu_hb->add_child(script_search_menu); script_search_menu->set_text(TTR("Search")); - 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", 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(); @@ -2738,6 +2812,10 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(file_dialog); file_dialog->connect("file_selected", this, "_file_dialog_action"); + error_dialog = memnew(AcceptDialog); + add_child(error_dialog); + error_dialog->get_ok()->set_text(TTR("I see..")); + debugger = memnew(ScriptEditorDebugger(editor)); debugger->connect("goto_script_line", this, "_goto_script_line"); debugger->connect("show_debugger", this, "_show_debugger"); @@ -2785,13 +2863,26 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(help_index); help_index->connect("open_class", this, "_help_class_open"); + find_in_files_dialog = memnew(FindInFilesDialog); + find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false)); + find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true)); + add_child(find_in_files_dialog); + find_in_files = memnew(FindInFilesPanel); + find_in_files_button = editor->add_bottom_panel_item(TTR("Search results"), find_in_files); + find_in_files_button->set_tooltip(TTR("Search in files")); + find_in_files->set_custom_minimum_size(Size2(0, 200)); + find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected"); + find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files"); + find_in_files->hide(); + find_in_files_button->hide(); + history_pos = -1; //debugger_gui->hide(); edit_pass = 0; - trim_trailing_whitespace_on_save = false; - convert_indent_on_save = false; - use_space_indentation = false; + trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save"); + convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save"); + use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type"); ScriptServer::edit_request_func = _open_script_request; @@ -2900,7 +2991,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) { EDITOR_DEF("text_editor/open_scripts/script_temperature_history_size", 15); EDITOR_DEF("text_editor/open_scripts/current_script_background_color", Color(1, 1, 1, 0.3)); EDITOR_DEF("text_editor/open_scripts/group_help_pages", true); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path")); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path,None")); EDITOR_DEF("text_editor/open_scripts/sort_scripts_by", 0); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path")); EDITOR_DEF("text_editor/open_scripts/list_script_names_as", 0); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index e60e4cf8c0..9f37b18d7d 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -80,6 +80,9 @@ protected: static void _bind_methods(); public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0; + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0; + virtual void apply_code() = 0; virtual Ref<Script> get_edited_script() const = 0; virtual Vector<String> get_functions() = 0; @@ -112,9 +115,12 @@ public: ScriptEditorBase() {} }; +typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)(); typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script); class EditorScriptCodeCompletionCache; +class FindInFilesDialog; +class FindInFilesPanel; class ScriptEditor : public PanelContainer { @@ -165,6 +171,7 @@ class ScriptEditor : public PanelContainer { enum ScriptSortBy { SORT_BY_NAME, SORT_BY_PATH, + SORT_BY_NONE }; enum ScriptListName { @@ -198,6 +205,7 @@ class ScriptEditor : public PanelContainer { VSplitContainer *list_split; TabContainer *tab_container; EditorFileDialog *file_dialog; + AcceptDialog *error_dialog; ConfirmationDialog *erase_tab_confirm; ScriptCreateDialog *script_create_dialog; ScriptEditorDebugger *debugger; @@ -211,13 +219,21 @@ class ScriptEditor : public PanelContainer { ToolButton *script_back; ToolButton *script_forward; + FindInFilesDialog *find_in_files_dialog; + FindInFilesPanel *find_in_files; + Button *find_in_files_button; + enum { - SCRIPT_EDITOR_FUNC_MAX = 32 + SCRIPT_EDITOR_FUNC_MAX = 32, + SYNTAX_HIGHLIGHTER_FUNC_MAX = 32 }; static int script_editor_func_count; static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX]; + static int syntax_highlighters_func_count; + static CreateSyntaxHighlighterFunc syntax_highlighters_funcs[SYNTAX_HIGHLIGHTER_FUNC_MAX]; + struct ScriptHistory { Control *control; @@ -227,8 +243,6 @@ class ScriptEditor : public PanelContainer { Vector<ScriptHistory> history; int history_pos; - Vector<String> previous_scripts; - EditorHelpIndex *help_index; void _tab_changed(int p_which); @@ -250,7 +264,9 @@ class ScriptEditor : public PanelContainer { void _update_recent_scripts(); void _open_recent_script(int p_idx); - void _close_tab(int p_idx, bool p_save = true); + void _show_error_dialog(String p_path); + + void _close_tab(int p_idx, bool p_save = true, bool p_history_back = true); void _close_current_tab(); void _close_discard_current_tab(const String &p_str); @@ -294,6 +310,8 @@ class ScriptEditor : public PanelContainer { void _update_window_menu(); void _script_created(Ref<Script> p_script); + ScriptEditorBase *_get_current_editor() const; + void _save_layout(); void _editor_settings_changed(); void _autosave_scripts(); @@ -349,6 +367,11 @@ class ScriptEditor : public PanelContainer { Ref<Script> _get_current_script(); Array _get_open_scripts() const; + void _on_find_in_files_requested(String text); + void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end); + void _start_find_in_files(bool with_replace); + void _on_find_in_files_modified_files(PoolStringArray paths); + static void _open_script_request(const String &p_path); static ScriptEditor *script_editor; @@ -397,7 +420,9 @@ public: ScriptEditorDebugger *get_debugger() { return debugger; } void set_live_auto_reload_running_scripts(bool p_enabled); + static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func); static void register_create_script_editor_function(CreateScriptEditorFunc p_func); + ScriptEditor(EditorNode *p_editor); ~ScriptEditor(); }; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index dffb98e488..45f5e667fa 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -349,7 +349,12 @@ void ScriptTextEditor::_convert_case(CaseStyle p_case) { int end_col = te->get_selection_to_column(); for (int i = begin; i <= end; i++) { - String new_line = te->get_line(i); + int len = te->get_line(i).length(); + if (i == end) + len -= len - end_col; + if (i == begin) + len -= begin_col; + String new_line = te->get_line(i).substr(i == begin ? begin_col : 0, len); switch (p_case) { case UPPER: { @@ -364,10 +369,10 @@ void ScriptTextEditor::_convert_case(CaseStyle p_case) { } if (i == begin) { - new_line = te->get_line(i).left(begin_col) + new_line.right(begin_col); + new_line = te->get_line(i).left(begin_col) + new_line; } if (i == end) { - new_line = new_line.left(end_col) + te->get_line(i).right(end_col); + new_line = new_line + te->get_line(i).right(end_col); } te->set_line(i, new_line); } @@ -519,10 +524,19 @@ void ScriptTextEditor::tag_saved_version() { void ScriptTextEditor::goto_line(int p_line, bool p_with_error) { TextEdit *tx = code_editor->get_text_edit(); + tx->deselect(); tx->unfold_line(p_line); tx->call_deferred("cursor_set_line", p_line); } +void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { + TextEdit *tx = code_editor->get_text_edit(); + tx->unfold_line(p_line); + tx->call_deferred("cursor_set_line", p_line); + tx->call_deferred("cursor_set_column", p_begin); + tx->select(p_line, p_begin, p_line, p_end); +} + void ScriptTextEditor::ensure_focus() { code_editor->get_text_edit()->grab_focus(); @@ -567,6 +581,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) { ERR_FAIL_COND(!script.is_null()); script = p_script; + _set_theme_for_script(); code_editor->get_text_edit()->set_text(script->get_source_code()); code_editor->get_text_edit()->clear_undo_history(); @@ -574,8 +589,6 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) { emit_signal("name_changed"); code_editor->update_line_and_column(); - - _set_theme_for_script(); } void ScriptTextEditor::_validate_script() { @@ -733,6 +746,8 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c _goto_line(p_row); + result.class_name = result.class_name.trim_prefix("_"); + switch (result.type) { case ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION: { @@ -783,6 +798,26 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c emit_signal("go_to_help", "class_method:" + result.class_name + ":" + result.class_member); } break; + case ScriptLanguage::LookupResult::RESULT_CLASS_ENUM: { + + StringName cname = result.class_name; + StringName success; + while (true) { + success = ClassDB::get_integer_constant_enum(cname, result.class_member, true); + if (success != StringName()) { + result.class_name = cname; + cname = ClassDB::get_parent_class(cname); + } else { + break; + } + } + + emit_signal("go_to_help", "class_enum:" + result.class_name + ":" + result.class_member); + + } break; + case ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE: { + emit_signal("go_to_help", "class_global:" + result.class_name + ":" + result.class_member); + } break; } } } @@ -934,13 +969,27 @@ void ScriptTextEditor::_edit_option(int p_op) { Ref<Script> scr = get_edited_script(); if (scr.is_null()) return; - tx->begin_complex_operation(); - int line = tx->cursor_get_line(); - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - tx->unfold_line(line); - tx->cursor_set_line(line); + if (tx->is_selection_active()) { + int to_line = tx->get_selection_to_line(); + int from_line = tx->get_selection_from_line(); + int count = Math::abs(to_line - from_line) + 1; + while (count) { + tx->set_line(tx->cursor_get_line(), ""); + tx->backspace_at_cursor(); + count--; + if (count) + tx->unfold_line(from_line); + } + tx->cursor_set_line(from_line - 1); + tx->deselect(); + } else { + int line = tx->cursor_get_line(); + tx->set_line(tx->cursor_get_line(), ""); + tx->backspace_at_cursor(); + tx->unfold_line(line); + tx->cursor_set_line(line); + } tx->end_complex_operation(); } break; case EDIT_CLONE_DOWN: { @@ -960,6 +1009,10 @@ void ScriptTextEditor::_edit_option(int p_op) { } int next_line = to_line + 1; + if (to_line >= tx->get_line_count() - 1) { + tx->set_line(to_line, tx->get_line(to_line) + "\n"); + } + tx->begin_complex_operation(); for (int i = from_line; i <= to_line; i++) { @@ -1134,6 +1187,15 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->get_find_replace_bar()->popup_replace(); } break; + case SEARCH_IN_FILES: { + + String selected_text = code_editor->get_text_edit()->get_selection_text(); + + // Yep, because it doesn't make sense to instance this dialog for every single script open... + // So this will be delegated to the ScriptEditor + emit_signal("search_in_files_requested", selected_text); + + } break; case SEARCH_LOCATE_FUNCTION: { quick_open->popup(get_functions()); @@ -1225,11 +1287,26 @@ void ScriptTextEditor::_edit_option(int p_op) { } } +void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + highlighters[p_highlighter->get_name()] = p_highlighter; + highlighter_menu->get_popup()->add_item(p_highlighter->get_name()); +} + +void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + TextEdit *te = code_editor->get_text_edit(); + te->_set_syntax_highlighting(p_highlighter); +} + +void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { + set_syntax_highlighter(highlighters[highlighter_menu->get_popup()->get_item_text(p_idx)]); +} + 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("_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); @@ -1283,12 +1360,9 @@ Variant ScriptTextEditor::get_drag_data_fw(const Point2 &p_point, Control *p_fro bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { Dictionary d = p_data; - if (d.has("type") && - ( - - String(d["type"]) == "resource" || - String(d["type"]) == "files" || - String(d["type"]) == "nodes")) { + if (d.has("type") && (String(d["type"]) == "resource" || + String(d["type"]) == "files" || + String(d["type"]) == "nodes")) { return true; } @@ -1329,6 +1403,10 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data Dictionary d = p_data; + TextEdit *te = code_editor->get_text_edit(); + int row, col; + te->_get_mouse_pos(p_point, row, col); + if (d.has("type") && String(d["type"]) == "resource") { Ref<Resource> res = d["resource"]; @@ -1341,7 +1419,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data return; } - code_editor->get_text_edit()->insert_text_at_cursor(res->get_path()); + te->cursor_set_line(row); + te->cursor_set_column(col); + te->insert_text_at_cursor(res->get_path()); } if (d.has("type") && String(d["type"]) == "files") { @@ -1356,7 +1436,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data text_to_drop += "\"" + String(files[i]).c_escape() + "\""; } - code_editor->get_text_edit()->insert_text_at_cursor(text_to_drop); + te->cursor_set_line(row); + te->cursor_set_column(col); + te->insert_text_at_cursor(text_to_drop); } if (d.has("type") && String(d["type"]) == "nodes") { @@ -1385,7 +1467,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data text_to_drop += "\"" + path.c_escape() + "\""; } - code_editor->get_text_edit()->insert_text_at_cursor(text_to_drop); + te->cursor_set_line(row); + te->cursor_set_column(col); + te->insert_text_at_cursor(text_to_drop); } } @@ -1565,16 +1649,12 @@ ScriptTextEditor::ScriptTextEditor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); edit_menu->get_popup()->add_separator(); -#ifdef OSX_ENABLED - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); -#else + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); -#endif edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); @@ -1603,6 +1683,8 @@ ScriptTextEditor::ScriptTextEditor() { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); search_menu->get_popup()->add_separator(); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES); + search_menu->get_popup()->add_separator(); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); search_menu->get_popup()->add_separator(); @@ -1612,6 +1694,14 @@ ScriptTextEditor::ScriptTextEditor() { edit_hb->add_child(edit_menu); + highlighters["Standard"] = NULL; + + highlighter_menu = memnew(MenuButton); + highlighter_menu->set_text(TTR("Syntax Highlighter")); + highlighter_menu->get_popup()->add_item("Standard"); + highlighter_menu->get_popup()->connect("id_pressed", this, "_change_syntax_highlighter"); + edit_hb->add_child(highlighter_menu); + quick_open = memnew(ScriptEditorQuickOpen); add_child(quick_open); quick_open->connect("goto_line", this, "_goto_line"); @@ -1645,13 +1735,14 @@ 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/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); 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); #ifdef OSX_ENABLED + 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/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_CTRL | KEY_MASK_ALT | KEY_T); @@ -1668,13 +1759,15 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Convert To Lowercase"), KEY_MASK_SHIFT | KEY_F3); ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F2); - ED_SHORTCUT("script_text_editor/find", TTR("Find.."), KEY_MASK_CMD | KEY_F); + ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F); ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3); ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3); - ED_SHORTCUT("script_text_editor/replace", TTR("Replace.."), KEY_MASK_CMD | KEY_R); + ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R); + + ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); - ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function.."), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_F); - ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD | KEY_L); + ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); + ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line..."), KEY_MASK_CMD | KEY_L); ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 22e8fbce25..a93e1a6fa8 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -49,6 +49,7 @@ class ScriptTextEditor : public ScriptEditorBase { HBoxContainer *edit_hb; MenuButton *edit_menu; + MenuButton *highlighter_menu; MenuButton *search_menu; PopupMenu *context_menu; @@ -105,6 +106,7 @@ class ScriptTextEditor : public ScriptEditorBase { SEARCH_REPLACE, SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, + SEARCH_IN_FILES, DEBUG_TOGGLE_BREAKPOINT, DEBUG_REMOVE_ALL_BREAKPOINTS, DEBUG_GOTO_NEXT_BREAKPOINT, @@ -125,6 +127,9 @@ protected: void _notification(int p_what); static void _bind_methods(); + Map<String, SyntaxHighlighter *> highlighters; + void _change_syntax_highlighter(int p_idx); + void _edit_option(int p_op); void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded); void _text_edit_gui_input(const Ref<InputEvent> &ev); @@ -145,6 +150,9 @@ protected: void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void apply_code(); virtual Ref<Script> get_edited_script() const; virtual Vector<String> get_functions(); @@ -163,6 +171,7 @@ public: virtual void tag_saved_version(); 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 reload(bool p_soft); virtual void get_breakpoints(List<int> *p_breakpoints); diff --git a/editor/plugins/shader_graph_editor_plugin.cpp b/editor/plugins/shader_graph_editor_plugin.cpp index 59085c203f..1a9d980feb 100644 --- a/editor/plugins/shader_graph_editor_plugin.cpp +++ b/editor/plugins/shader_graph_editor_plugin.cpp @@ -1478,7 +1478,7 @@ void ShaderGraphView::_create_node(int p_id) { case ShaderGraph::NODE_XFORM_CONST: { gn->set_title("XForm"); ToolButton *edit = memnew( ToolButton ); - edit->set_text("edit.."); + edit->set_text("edit..."); edit->connect("pressed",this,"_xform_const_changed",varray(p_id,edit)); gn->add_child(edit); gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]); @@ -2289,7 +2289,7 @@ void ShaderGraphView::_create_node(int p_id) { le->set_text(graph->input_node_get_name(type,p_id)); le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); ToolButton *edit = memnew( ToolButton ); - edit->set_text("edit.."); + edit->set_text("edit..."); edit->connect("pressed",this,"_xform_input_changed",varray(p_id,edit)); gn->add_child(edit); gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]); @@ -2310,7 +2310,7 @@ void ShaderGraphView::_create_node(int p_id) { tex->set_mouse_filter(MOUSE_FILTER_PASS); tex->set_texture(graph->texture_input_node_get_value(type,p_id)); ToolButton *edit = memnew( ToolButton ); - edit->set_text("edit.."); + edit->set_text("edit..."); edit->connect("pressed",this,"_tex_edited",varray(p_id,edit)); gn->add_child(edit); @@ -2345,7 +2345,7 @@ void ShaderGraphView::_create_node(int p_id) { le->connect("text_entered",this,"_input_name_changed",varray(p_id,le)); ToolButton *edit = memnew( ToolButton ); - edit->set_text("edit.."); + edit->set_text("edit..."); edit->connect("pressed",this,"_cube_edited",varray(p_id,edit)); gn->add_child(edit); @@ -2769,8 +2769,6 @@ void ShaderGraphEditor::_popup_requested(const Vector2 &p_position) popup->set_global_position(p_position); popup->set_size( Size2( 200, 0) ); popup->popup(); - popup->call_deferred("grab_click_focus"); - popup->set_invalidate_click_until_motion(); } void ShaderGraphEditor::_notification(int p_what) { diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp new file mode 100644 index 0000000000..e372f792d6 --- /dev/null +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -0,0 +1,120 @@ +#include "skeleton_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "scene/2d/mesh_instance_2d.h" +#include "scene/gui/box_container.h" +#include "thirdparty/misc/clipper.hpp" + +void Skeleton2DEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + options->hide(); + } +} + +void Skeleton2DEditor::edit(Skeleton2D *p_sprite) { + + node = p_sprite; +} + +void Skeleton2DEditor::_menu_option(int p_option) { + + if (!node) { + return; + } + + switch (p_option) { + case MENU_OPTION_MAKE_REST: { + + if (node->get_bone_count() == 0) { + err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes.")); + err_dialog->popup_centered_minsize(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("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()); + ur->add_undo_method(bone, "set_rest", bone->get_rest()); + } + ur->commit_action(); + + } break; + case MENU_OPTION_SET_REST: { + if (node->get_bone_count() == 0) { + err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes.")); + err_dialog->popup_centered_minsize(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action("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()); + ur->add_undo_method(bone, "set_transform", bone->get_transform()); + } + ur->commit_action(); + + } break; + } +} + +void Skeleton2DEditor::_bind_methods() { + + ClassDB::bind_method("_menu_option", &Skeleton2DEditor::_menu_option); +} + +Skeleton2DEditor::Skeleton2DEditor() { + + options = memnew(MenuButton); + + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options); + + options->set_text(TTR("Skeleton2D")); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton2D", "EditorIcons")); + + 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->get_popup()->connect("id_pressed", this, "_menu_option"); + + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); +} + +void Skeleton2DEditorPlugin::edit(Object *p_object) { + + sprite_editor->edit(Object::cast_to<Skeleton2D>(p_object)); +} + +bool Skeleton2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("Skeleton2D"); +} + +void Skeleton2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + sprite_editor->options->show(); + } else { + + sprite_editor->options->hide(); + sprite_editor->edit(NULL); + } +} + +Skeleton2DEditorPlugin::Skeleton2DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + sprite_editor = memnew(Skeleton2DEditor); + editor->get_viewport()->add_child(sprite_editor); + make_visible(false); + + //sprite_editor->options->hide(); +} + +Skeleton2DEditorPlugin::~Skeleton2DEditorPlugin() { +} diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h new file mode 100644 index 0000000000..bbe2a3a6f2 --- /dev/null +++ b/editor/plugins/skeleton_2d_editor_plugin.h @@ -0,0 +1,55 @@ +#ifndef SKELETON_2D_EDITOR_PLUGIN_H +#define SKELETON_2D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/2d/skeleton_2d.h" +#include "scene/gui/spin_box.h" + +class Skeleton2DEditor : public Control { + + GDCLASS(Skeleton2DEditor, Control); + + enum Menu { + MENU_OPTION_MAKE_REST, + MENU_OPTION_SET_REST, + }; + + Skeleton2D *node; + + MenuButton *options; + AcceptDialog *err_dialog; + + void _menu_option(int p_option); + + //void _create_uv_lines(); + friend class Skeleton2DEditorPlugin; + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(Skeleton2D *p_sprite); + Skeleton2DEditor(); +}; + +class Skeleton2DEditorPlugin : public EditorPlugin { + + GDCLASS(Skeleton2DEditorPlugin, EditorPlugin); + + Skeleton2DEditor *sprite_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "Skeleton2D"; } + 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); + + Skeleton2DEditorPlugin(EditorNode *p_node); + ~Skeleton2DEditorPlugin(); +}; + +#endif // SKELETON_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp new file mode 100644 index 0000000000..c41e3b546f --- /dev/null +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -0,0 +1,189 @@ +/*************************************************************************/ +/* skeleton_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "skeleton_editor_plugin.h" +#include "scene/3d/collision_shape.h" +#include "scene/3d/physics_body.h" +#include "scene/3d/physics_joint.h"; +#include "scene/resources/capsule_shape.h" +#include "scene/resources/sphere_shape.h" +#include "spatial_editor_plugin.h" + +void SkeletonEditor::_on_click_option(int p_option) { + if (!skeleton) { + return; + } + + switch (p_option) { + case MENU_OPTION_CREATE_PHYSICAL_SKELETON: { + create_physical_skeleton(); + } break; + } +} + +void SkeletonEditor::create_physical_skeleton() { + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner(); + + const int bc = skeleton->get_bone_count(); + + if (!bc) { + return; + } + + Vector<BoneInfo> bones_infos; + bones_infos.resize(bc); + + for (int bone_id = 0; bc > bone_id; ++bone_id) { + + const int parent = skeleton->get_bone_parent(bone_id); + const int parent_parent = skeleton->get_bone_parent(parent); + + if (parent < 0) { + + bones_infos[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); + + } else { + + bones_infos[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); + + /// create physical bone on parent + if (!bones_infos[parent].physical_bone) { + + bones_infos[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos); + + ur->create_action(TTR("Create physical bones")); + ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone); + ur->add_do_reference(bones_infos[parent].physical_bone); + ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone); + ur->commit_action(); + + bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent)); + bones_infos[parent].physical_bone->set_owner(owner); + bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner + + /// Create joint between parent of parent + if (-1 != parent_parent) { + + bones_infos[parent].physical_bone->set_joint_type(PhysicalBone::JOINT_TYPE_PIN); + } + } + } + } +} + +PhysicalBone *SkeletonEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) { + + real_t half_height(skeleton->get_bone_rest(bone_child_id).origin.length() * 0.5); + real_t radius(half_height * 0.2); + + CapsuleShape *bone_shape_capsule = memnew(CapsuleShape); + bone_shape_capsule->set_height((half_height - radius) * 2); + bone_shape_capsule->set_radius(radius); + + CollisionShape *bone_shape = memnew(CollisionShape); + bone_shape->set_shape(bone_shape_capsule); + + Transform body_transform; + body_transform.origin = Vector3(0, 0, -half_height); + + Transform joint_transform; + joint_transform.origin = Vector3(0, 0, half_height); + + PhysicalBone *physical_bone = memnew(PhysicalBone); + physical_bone->add_child(bone_shape); + physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id)); + physical_bone->set_body_offset(body_transform); + physical_bone->set_joint_offset(joint_transform); + return physical_bone; +} + +void SkeletonEditor::edit(Skeleton *p_node) { + skeleton = p_node; +} + +void SkeletonEditor::_node_removed(Node *p_node) { + + if (p_node == skeleton) { + skeleton = NULL; + options->hide(); + } +} + +void SkeletonEditor::_bind_methods() { + ClassDB::bind_method("_on_click_option", &SkeletonEditor::_on_click_option); +} + +SkeletonEditor::SkeletonEditor() { + options = memnew(MenuButton); + SpatialEditor::get_singleton()->add_control_to_menu_panel(options); + + options->set_text(TTR("Skeleton")); + options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton", "EditorIcons")); + + options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON); + + options->get_popup()->connect("id_pressed", this, "_on_click_option"); + options->hide(); +} + +SkeletonEditor::~SkeletonEditor() { + SpatialEditor::get_singleton()->remove_child(options); + memdelete(options); +} + +void SkeletonEditorPlugin::edit(Object *p_object) { + skeleton_editor->edit(Object::cast_to<Skeleton>(p_object)); +} + +bool SkeletonEditorPlugin::handles(Object *p_object) const { + return p_object->is_class("Skeleton"); +} + +void SkeletonEditorPlugin::make_visible(bool p_visible) { + if (p_visible) { + skeleton_editor->options->show(); + } else { + + skeleton_editor->options->hide(); + skeleton_editor->edit(NULL); + } +} + +SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) { + editor = p_node; + skeleton_editor = memnew(SkeletonEditor); + editor->get_viewport()->add_child(skeleton_editor); +} + +SkeletonEditorPlugin::~SkeletonEditorPlugin() { + editor->get_viewport()->remove_child(skeleton_editor); + memdelete(skeleton_editor); +} diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h new file mode 100644 index 0000000000..b9bdf91902 --- /dev/null +++ b/editor/plugins/skeleton_editor_plugin.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* skeleton_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 SKELETON_EDITOR_PLUGIN_H +#define SKELETON_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/skeleton.h" + +class PhysicalBone; +class Joint; + +class SkeletonEditor : public Node { + GDCLASS(SkeletonEditor, Node); + + enum Menu { + MENU_OPTION_CREATE_PHYSICAL_SKELETON + }; + + struct BoneInfo { + PhysicalBone *physical_bone; + Transform relative_rest; // Relative to skeleton node + BoneInfo() : + physical_bone(NULL) {} + }; + + Skeleton *skeleton; + + MenuButton *options; + + void _on_click_option(int p_option); + + friend class SkeletonEditorPlugin; + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + + void create_physical_skeleton(); + PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos); + +public: + void edit(Skeleton *p_mesh); + + SkeletonEditor(); + ~SkeletonEditor(); +}; + +class SkeletonEditorPlugin : public EditorPlugin { + + GDCLASS(SkeletonEditorPlugin, EditorPlugin); + + EditorNode *editor; + SkeletonEditor *skeleton_editor; + +public: + virtual String get_name() const { return "Skeleton"; } + virtual 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); + + SkeletonEditorPlugin(EditorNode *p_node); + ~SkeletonEditorPlugin(); +}; + +#endif // SKELETON_EDITOR_PLUGIN_H diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 5e8eb06556..5b713ef3c4 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -72,6 +72,14 @@ #define MIN_FOV 0.01 #define MAX_FOV 179 +#ifdef TOOLS_ENABLED +#define get_global_gizmo_transform get_global_gizmo_transform +#define get_local_gizmo_transform get_local_gizmo_transform +#else +#define get_global_gizmo_transform get_global_transform +#define get_local_gizmo_transform get_transform +#endif + void SpatialEditorViewport::_update_camera(float p_interp_delta) { bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL; @@ -318,6 +326,9 @@ void SpatialEditorViewport::_select(Spatial *p_node, bool p_append, bool p_singl editor_selection->clear(); editor_selection->add_node(p_node); + if (Engine::get_singleton()->is_editor_hint()) + editor->call("edit_node", p_node); + } else { if (editor_selection->is_selected(p_node) && p_single) { @@ -581,8 +592,8 @@ void SpatialEditorViewport::_compute_edit(const Point2 &p_point) { if (!se) continue; - se->original = se->sp->get_global_transform(); - se->original_local = se->sp->get_transform(); + se->original = se->sp->get_global_gizmo_transform(); + se->original_local = se->sp->get_local_gizmo_transform(); } } @@ -883,8 +894,6 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) { selection_menu->set_global_position(b->get_global_position()); selection_menu->popup(); - selection_menu->call_deferred("grab_click_focus"); - selection_menu->set_invalidate_click_until_motion(); } } @@ -1183,7 +1192,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (!se) continue; - undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_transform()); + undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); undo_redo->add_undo_method(sp, "set_global_transform", se->original); } undo_redo->commit_action(); @@ -1904,8 +1913,13 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); - cursor.x_rot += p_relative.y * radians_per_pixel; + if (invert_y_axis) { + cursor.x_rot -= p_relative.y * radians_per_pixel; + } else { + cursor.x_rot += p_relative.y * radians_per_pixel; + } cursor.y_rot += p_relative.x * radians_per_pixel; if (cursor.x_rot > Math_PI / 2.0) cursor.x_rot = Math_PI / 2.0; @@ -1922,11 +1936,16 @@ void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, cons if (!orthogonal) { real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". Transform prev_camera_transform = to_camera_transform(cursor); - cursor.x_rot += p_relative.y * radians_per_pixel; + if (invert_y_axis) { + cursor.x_rot -= p_relative.y * radians_per_pixel; + } else { + cursor.x_rot += p_relative.y * radians_per_pixel; + } cursor.y_rot += p_relative.x * radians_per_pixel; if (cursor.x_rot > Math_PI / 2.0) cursor.x_rot = Math_PI / 2.0; @@ -2139,7 +2158,7 @@ void SpatialEditorViewport::_notification(int p_what) { se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); } - Transform t = sp->get_global_transform(); + Transform t = sp->get_global_gizmo_transform(); t.translate(se->aabb.position); // apply AABB scaling before item's global transform @@ -2492,7 +2511,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { xform.scale_basis(sp->get_scale()); undo_redo->add_do_method(sp, "set_global_transform", xform); - undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform()); + undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); } undo_redo->commit_action(); } break; @@ -2950,7 +2969,7 @@ void SpatialEditorViewport::focus_selection() { if (!se) continue; - center += sp->get_global_transform().origin; + center += sp->get_global_gizmo_transform().origin; count++; } @@ -3032,7 +3051,7 @@ AABB SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, c MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(child); if (mesh_instance) { AABB mesh_instance_bounds = mesh_instance->get_aabb(); - mesh_instance_bounds.position += mesh_instance->get_global_transform().origin - p_parent->get_global_transform().origin; + mesh_instance_bounds.position += mesh_instance->get_global_gizmo_transform().origin - p_parent->get_global_gizmo_transform().origin; bounds.merge_with(mesh_instance_bounds); } bounds = _calculate_spatial_bounds(child, bounds); @@ -3110,7 +3129,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P if (!scene.is_valid()) { // invalid scene return false; } else { - instanced_scene = scene->instance(); + instanced_scene = scene->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); } } } @@ -3143,7 +3162,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P Transform global_transform; Spatial *parent_spatial = Object::cast_to<Spatial>(parent); if (parent_spatial) - global_transform = parent_spatial->get_global_transform(); + global_transform = parent_spatial->get_global_gizmo_transform(); global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); @@ -3272,7 +3291,7 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p } } if (list.size() != 1) { - accept->get_ok()->set_text(TTR("I see..")); + accept->get_ok()->set_text(TTR("I see...")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); _remove_preview(); @@ -3340,14 +3359,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR); view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); - view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); + view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); + view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); view_menu->get_popup()->add_separator(); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); - view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); + view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true); view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT); @@ -3776,7 +3795,8 @@ void SpatialEditor::update_transform_gizmo() { if (!se) continue; - Transform xf = se->sp->get_global_transform(); + Transform xf = se->sp->get_global_gizmo_transform(); + if (first) { center.position = xf.origin; first = false; @@ -3827,9 +3847,6 @@ Object *SpatialEditor::_get_editor_data(Object *p_what) { si->sbox_instance = VisualServer::get_singleton()->instance_create2(selection_box->get_rid(), sp->get_world()->get_scenario()); VS::get_singleton()->instance_geometry_set_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF); - if (Engine::get_singleton()->is_editor_hint()) - editor->call("edit_node", sp); - return si; } @@ -4046,7 +4063,7 @@ void SpatialEditor::_xform_dialog_action() { bool post = xform_type->get_selected() > 0; - Transform tr = sp->get_global_transform(); + Transform tr = sp->get_global_gizmo_transform(); if (post) tr = tr * t; else { @@ -4056,7 +4073,7 @@ void SpatialEditor::_xform_dialog_action() { } undo_redo->add_do_method(sp, "set_global_transform", tr); - undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform()); + undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); } undo_redo->commit_action(); } @@ -4282,67 +4299,26 @@ void SpatialEditor::_init_indicators() { indicator_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); indicator_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - PoolVector<Color> grid_colors[3]; - PoolVector<Vector3> grid_points[3]; Vector<Color> origin_colors; Vector<Vector3> origin_points; - Color grid_color = EditorSettings::get_singleton()->get("editors/3d/grid_color"); - for (int i = 0; i < 3; i++) { Vector3 axis; axis[i] = 1; - Vector3 axis_n1; - axis_n1[(i + 1) % 3] = 1; - Vector3 axis_n2; - axis_n2[(i + 2) % 3] = 1; + + grid_enable[i] = false; + grid_visible[i] = false; origin_colors.push_back(Color(axis.x, axis.y, axis.z)); origin_colors.push_back(Color(axis.x, axis.y, axis.z)); origin_points.push_back(axis * 4096); origin_points.push_back(axis * -4096); -#define ORIGIN_GRID_SIZE 50 - - for (int j = -ORIGIN_GRID_SIZE; j <= ORIGIN_GRID_SIZE; j++) { - - Vector3 p1 = axis_n1 * j + axis_n2 * -ORIGIN_GRID_SIZE; - Vector3 p1_dest = p1 * (-axis_n2 + axis_n1); - Vector3 p2 = axis_n2 * j + axis_n1 * -ORIGIN_GRID_SIZE; - Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); - - Color line_color = grid_color; - if (j == 0) { - continue; - } else if (j % 10 == 0) { - line_color *= 1.5; - } - - grid_points[i].push_back(p1); - grid_points[i].push_back(p1_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); - - grid_points[i].push_back(p2); - grid_points[i].push_back(p2_dest); - grid_colors[i].push_back(line_color); - grid_colors[i].push_back(line_color); - } + } - grid[i] = VisualServer::get_singleton()->mesh_create(); - Array d; - d.resize(VS::ARRAY_MAX); - d[VisualServer::ARRAY_VERTEX] = grid_points[i]; - d[VisualServer::ARRAY_COLOR] = grid_colors[i]; - VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d); - VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid()); - grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario()); + grid_enable[1] = true; + grid_visible[1] = true; - grid_visible[i] = false; - grid_enable[i] = false; - VisualServer::get_singleton()->instance_set_visible(grid_instance[i], false); - VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << SpatialEditorViewport::GIZMO_GRID_LAYER); - } + _init_grid(); origin = VisualServer::get_singleton()->mesh_create(); Array d; @@ -4358,9 +4334,6 @@ void SpatialEditor::_init_indicators() { VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF); - VisualServer::get_singleton()->instance_set_visible(grid_instance[1], true); - grid_enable[1] = true; - grid_visible[1] = true; grid_enabled = true; last_grid_snap = 1; } @@ -4629,10 +4602,72 @@ void SpatialEditor::_init_indicators() { _generate_selection_box(); } +void SpatialEditor::_init_grid() { + + PoolVector<Color> grid_colors[3]; + PoolVector<Vector3> grid_points[3]; + + Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color"); + Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color"); + int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size"); + int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps"); + + for (int i = 0; i < 3; i++) { + Vector3 axis; + axis[i] = 1; + Vector3 axis_n1; + axis_n1[(i + 1) % 3] = 1; + Vector3 axis_n2; + axis_n2[(i + 2) % 3] = 1; + + for (int j = -grid_size; j <= grid_size; j++) { + Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size; + Vector3 p1_dest = p1 * (-axis_n2 + axis_n1); + Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size; + Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); + + Color line_color = secondary_grid_color; + if (j == 0) { + continue; + } else if (j % primary_grid_steps == 0) { + line_color = primary_grid_color; + } + + grid_points[i].push_back(p1); + grid_points[i].push_back(p1_dest); + grid_colors[i].push_back(line_color); + grid_colors[i].push_back(line_color); + + grid_points[i].push_back(p2); + grid_points[i].push_back(p2_dest); + grid_colors[i].push_back(line_color); + grid_colors[i].push_back(line_color); + } + + grid[i] = VisualServer::get_singleton()->mesh_create(); + Array d; + d.resize(VS::ARRAY_MAX); + d[VisualServer::ARRAY_VERTEX] = grid_points[i]; + d[VisualServer::ARRAY_COLOR] = grid_colors[i]; + VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d); + VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid()); + grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario()); + + VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]); + VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF); + VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << SpatialEditorViewport::GIZMO_GRID_LAYER); + } +} + void SpatialEditor::_finish_indicators() { VisualServer::get_singleton()->free(origin_instance); VisualServer::get_singleton()->free(origin); + + _finish_grid(); +} + +void SpatialEditor::_finish_grid() { for (int i = 0; i < 3; i++) { VisualServer::get_singleton()->free(grid_instance[i]); VisualServer::get_singleton()->free(grid[i]); @@ -4674,6 +4709,8 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) { if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack()) return; + snap_key_enabled = Input::get_singleton()->is_key_pressed(KEY_CONTROL); + Ref<InputEventKey> k = p_event; if (k.is_valid()) { @@ -4770,6 +4807,10 @@ void SpatialEditor::_notification(int p_what) { view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons")); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons")); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons")); + + // Update grid color by rebuilding grid. + _finish_grid(); + _init_grid(); } } @@ -4778,6 +4819,11 @@ void SpatialEditor::add_control_to_menu_panel(Control *p_control) { hbc_menu->add_child(p_control); } +void SpatialEditor::remove_control_from_menu_panel(Control *p_control) { + + hbc_menu->remove_child(p_control); +} + void SpatialEditor::set_can_preview(Camera *p_preview) { for (int i = 0; i < 4; i++) { @@ -4936,6 +4982,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { editor_selection->add_editor_plugin(this); snap_enabled = false; + snap_key_enabled = false; tool_mode = TOOL_MODE_SELECT; hbc_menu = memnew(HBoxContainer); @@ -5045,8 +5092,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Tool Rotate"), KEY_E); ED_SHORTCUT("spatial_editor/tool_scale", TTR("Tool Scale"), KEY_R); - ED_SHORTCUT("spatial_editor/display_wireframe", TTR("Display Wireframe"), KEY_Z); - ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F); PopupMenu *p; @@ -5056,9 +5101,9 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(transform_menu); p = transform_menu->get_popup(); - p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap..")), MENU_TRANSFORM_CONFIGURE_SNAP); + 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); + p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); p->connect("id_pressed", this, "_menu_item_pressed"); @@ -5072,12 +5117,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { accept = memnew(AcceptDialog); editor->get_gui_base()->add_child(accept); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT); - p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT); + p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); p->add_separator(); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index e12f7affb7..7736db67b1 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -505,6 +505,7 @@ private: ConfirmationDialog *settings_dialog; bool snap_enabled; + bool snap_key_enabled; LineEdit *snap_translate; LineEdit *snap_rotate; LineEdit *snap_scale; @@ -531,7 +532,9 @@ private: void _instance_scene(); void _init_indicators(); + void _init_grid(); void _finish_indicators(); + void _finish_grid(); void _toggle_maximize_view(Object *p_viewport); @@ -577,7 +580,7 @@ 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; } + 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(); } @@ -605,6 +608,7 @@ public: UndoRedo *get_undo_redo() { return undo_redo; } void add_control_to_menu_panel(Control *p_control); + void remove_control_from_menu_panel(Control *p_control); VSplitContainer *get_shader_split(); HSplitContainer *get_palette_split(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp new file mode 100644 index 0000000000..49816fe2ae --- /dev/null +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -0,0 +1,402 @@ +#include "sprite_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "scene/2d/mesh_instance_2d.h" +#include "scene/gui/box_container.h" +#include "thirdparty/misc/clipper.hpp" + +void SpriteEditor::_node_removed(Node *p_node) { + + if (p_node == node) { + node = NULL; + options->hide(); + } +} + +void SpriteEditor::edit(Sprite *p_sprite) { + + node = p_sprite; +} + +#define PRECISION 10.0 + +Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float epsilon = 2.0) { + int size = points.size(); + ERR_FAIL_COND_V(size < 2, Vector<Vector2>()); + + ClipperLib::Path subj; + ClipperLib::PolyTree solution; + ClipperLib::PolyTree out; + + for (int i = 0; i < points.size(); i++) { + + subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION); + } + ClipperLib::ClipperOffset co; + co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); + co.Execute(solution, epsilon * PRECISION); + + ClipperLib::PolyNode *p = solution.GetFirst(); + + ERR_FAIL_COND_V(!p, points); + + while (p->IsHole()) { + p = p->GetNext(); + } + + //turn the result into simply polygon (AKA, fix overlap) + + //clamp into the specified rect + ClipperLib::Clipper cl; + cl.StrictlySimple(true); + cl.AddPath(p->Contour, ClipperLib::ptSubject, true); + //create the clipping rect + ClipperLib::Path clamp; + clamp.push_back(ClipperLib::IntPoint(0, 0)); + clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0)); + clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION)); + clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION)); + cl.AddPath(clamp, ClipperLib::ptClip, true); + cl.Execute(ClipperLib::ctIntersection, out); + + Vector<Vector2> outPoints; + ClipperLib::PolyNode *p2 = out.GetFirst(); + while (p2->IsHole()) { + p2 = p2->GetNext(); + } + + int lasti = p2->Contour.size() - 1; + Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION); + for (int i = 0; i < p2->Contour.size(); i++) { + + Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION); + if (cur.distance_to(prev) > 0.5) { + outPoints.push_back(cur); + prev = cur; + } + } + return outPoints; +} + +void SpriteEditor::_menu_option(int p_option) { + + if (!node) { + return; + } + + switch (p_option) { + case MENU_OPTION_CREATE_MESH_2D: { + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + } +} + +void SpriteEditor::_update_mesh_data() { + + Ref<Texture> texture = node->get_texture(); + if (texture.is_null()) { + err_dialog->set_text(TTR("Sprite is empty!")); + err_dialog->popup_centered_minsize(); + return; + } + + if (node->get_hframes() > 1 || node->get_vframes() > 1) { + err_dialog->set_text(TTR("Can't convert a sprite using animation frames to mesh.")); + err_dialog->popup_centered_minsize(); + return; + } + Ref<Image> image = texture->get_data(); + ERR_FAIL_COND(image.is_null()); + Rect2 rect; + if (node->is_region()) + rect = node->get_region_rect(); + else + rect.size = Size2(image->get_width(), image->get_height()); + + Ref<BitMap> bm; + bm.instance(); + bm->create_from_image_alpha(image); + + int grow = island_merging->get_value(); + if (grow > 0) { + bm->grow_mask(grow, rect); + } + + float epsilon = simplification->get_value(); + + Vector<Vector<Vector2> > lines = bm->clip_opaque_to_polygons(rect, epsilon); + + print_line("lines: " + itos(lines.size())); + uv_lines.clear(); + + computed_vertices.clear(); + computed_uv.clear(); + computed_indices.clear(); + + Size2 img_size = Vector2(image->get_width(), image->get_height()); + for (int j = 0; j < lines.size(); j++) { + lines[j] = expand(lines[j], rect, epsilon); + + 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); + + 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; + + if (node->is_centered()) + vtx -= rect.size / 2.0; + + computed_vertices.push_back(vtx); + } +#if 0 + Vector<Vector<Vector2> > polys = Geometry::decompose_polygon(lines[j]); + print_line("polygon: " + itos(polys.size())); + + for (int i = 0; i < polys.size(); i++) { + for (int k = 0; k < polys[i].size(); k++) { + + int idxn = (k + 1) % polys[i].size(); + uv_lines.push_back(polys[i][k]); + uv_lines.push_back(polys[i][idxn]); + } + } +#endif + +#if 1 + + Vector<int> poly = Geometry::triangulate_polygon(lines[j]); + + 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); + } + } +#endif + +#if 0 + for (int i = 0; i < lines[j].size() - 1; i++) { + uv_lines.push_back(lines[j][i]); + uv_lines.push_back(lines[j][i + 1]); + } +#endif + } + + debug_uv->update(); +} + +void SpriteEditor::_create_mesh_node() { + + if (computed_vertices.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh.")); + err_dialog->popup_centered_minsize(); + return; + } + + Ref<ArrayMesh> mesh; + mesh.instance(); + + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = computed_vertices; + a[Mesh::ARRAY_TEX_UV] = computed_uv; + a[Mesh::ARRAY_INDEX] = computed_indices; + + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + MeshInstance2D *mesh_instance = memnew(MeshInstance2D); + mesh_instance->set_mesh(mesh); + EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance); +} + +#if 0 +void SpriteEditor::_create_uv_lines() { + + Ref<Mesh> sprite = node->get_sprite(); + ERR_FAIL_COND(!sprite.is_valid()); + + Set<SpriteEditorEdgeSort> edges; + uv_lines.clear(); + for (int i = 0; i < sprite->get_surface_count(); i++) { + if (sprite->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) + continue; + Array a = sprite->surface_get_arrays(i); + + PoolVector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2]; + if (uv.size() == 0) { + err_dialog->set_text(TTR("Model has no UV in this layer")); + err_dialog->popup_centered_minsize(); + return; + } + + PoolVector<Vector2>::Read r = uv.read(); + + PoolVector<int> indices = a[Mesh::ARRAY_INDEX]; + PoolVector<int>::Read ri; + + int ic; + bool use_indices; + + if (indices.size()) { + ic = indices.size(); + ri = indices.read(); + use_indices = true; + } else { + ic = uv.size(); + use_indices = false; + } + + for (int j = 0; j < ic; j += 3) { + + for (int k = 0; k < 3; k++) { + + SpriteEditorEdgeSort edge; + if (use_indices) { + edge.a = r[ri[j + k]]; + edge.b = r[ri[j + ((k + 1) % 3)]]; + } else { + edge.a = r[j + k]; + edge.b = r[j + ((k + 1) % 3)]; + } + + if (edges.has(edge)) + continue; + + uv_lines.push_back(edge.a); + uv_lines.push_back(edge.b); + edges.insert(edge); + } + } + } + + debug_uv_dialog->popup_centered_minsize(); +} +#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)); +} + +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); +} + +SpriteEditor::SpriteEditor() { + + options = memnew(MenuButton); + + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options); + + 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()->connect("id_pressed", this, "_menu_option"); + + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); + + debug_uv_dialog = memnew(ConfirmationDialog); + debug_uv_dialog->get_ok()->set_text(TTR("Create 2D Mesh")); + debug_uv_dialog->set_title("Mesh 2D Preview"); + VBoxContainer *vb = memnew(VBoxContainer); + debug_uv_dialog->add_child(vb); + ScrollContainer *scroll = memnew(ScrollContainer); + scroll->set_custom_minimum_size(Size2(800, 500) * EDSCALE); + scroll->set_enable_h_scroll(true); + scroll->set_enable_v_scroll(true); + vb->add_margin_child(TTR("Preview:"), scroll, true); + debug_uv = memnew(Control); + debug_uv->connect("draw", this, "_debug_uv_draw"); + scroll->add_child(debug_uv); + debug_uv_dialog->connect("confirmed", this, "_create_mesh_node"); + + HBoxContainer *hb = memnew(HBoxContainer); + hb->add_child(memnew(Label(TTR("Simplification: ")))); + simplification = memnew(SpinBox); + simplification->set_min(0.01); + simplification->set_max(10.00); + simplification->set_step(0.01); + simplification->set_value(2); + hb->add_child(simplification); + hb->add_spacer(); + hb->add_child(memnew(Label(TTR("Grow (Pixels): ")))); + island_merging = memnew(SpinBox); + island_merging->set_min(0); + island_merging->set_max(10); + island_merging->set_step(1); + island_merging->set_value(2); + hb->add_child(island_merging); + hb->add_spacer(); + update_preview = memnew(Button); + update_preview->set_text(TTR("Update Preview")); + update_preview->connect("pressed", this, "_update_mesh_data"); + hb->add_child(update_preview); + vb->add_margin_child(TTR("Settings:"), hb); + + add_child(debug_uv_dialog); +} + +void SpriteEditorPlugin::edit(Object *p_object) { + + sprite_editor->edit(Object::cast_to<Sprite>(p_object)); +} + +bool SpriteEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("Sprite"); +} + +void SpriteEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + sprite_editor->options->show(); + } else { + + sprite_editor->options->hide(); + sprite_editor->edit(NULL); + } +} + +SpriteEditorPlugin::SpriteEditorPlugin(EditorNode *p_node) { + + editor = p_node; + sprite_editor = memnew(SpriteEditor); + editor->get_viewport()->add_child(sprite_editor); + make_visible(false); + + //sprite_editor->options->hide(); +} + +SpriteEditorPlugin::~SpriteEditorPlugin() { +} diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h new file mode 100644 index 0000000000..17aa3eb1f9 --- /dev/null +++ b/editor/plugins/sprite_editor_plugin.h @@ -0,0 +1,73 @@ +#ifndef SPRITE_EDITOR_PLUGIN_H +#define SPRITE_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/2d/sprite.h" +#include "scene/gui/spin_box.h" + +class SpriteEditor : public Control { + + GDCLASS(SpriteEditor, Control); + + enum Menu { + MENU_OPTION_CREATE_MESH_2D, + }; + + Sprite *node; + + MenuButton *options; + + ConfirmationDialog *outline_dialog; + + AcceptDialog *err_dialog; + + ConfirmationDialog *debug_uv_dialog; + Control *debug_uv; + Vector<Vector2> uv_lines; + + Vector<Vector2> computed_vertices; + Vector<Vector2> computed_uv; + Vector<int> computed_indices; + + SpinBox *simplification; + SpinBox *island_merging; + Button *update_preview; + + void _menu_option(int p_option); + + //void _create_uv_lines(); + friend class SpriteEditorPlugin; + + void _debug_uv_draw(); + void _update_mesh_data(); + void _create_mesh_node(); + +protected: + void _node_removed(Node *p_node); + static void _bind_methods(); + +public: + void edit(Sprite *p_sprite); + SpriteEditor(); +}; + +class SpriteEditorPlugin : public EditorPlugin { + + GDCLASS(SpriteEditorPlugin, EditorPlugin); + + SpriteEditor *sprite_editor; + EditorNode *editor; + +public: + virtual String get_name() const { return "Sprite"; } + 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); + + SpriteEditorPlugin(EditorNode *p_node); + ~SpriteEditorPlugin(); +}; + +#endif // SPRITE_EDITOR_PLUGIN_H diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 7a4eee0344..a9afc7a670 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -45,6 +45,12 @@ void SpriteFramesEditor::_notification(int p_what) { 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")); @@ -398,8 +404,6 @@ void SpriteFramesEditor::_animation_add() { animations->grab_focus(); } void SpriteFramesEditor::_animation_remove() { - - //fuck everything if (updating) return; @@ -460,8 +464,6 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { List<StringName> anim_names; - anim_names.sort_custom<StringName::AlphCompare>(); - frames->get_animation_list(&anim_names); anim_names.sort_custom<StringName::AlphCompare>(); @@ -740,27 +742,35 @@ SpriteFramesEditor::SpriteFramesEditor() { hbc->add_child(load); copy = memnew(Button); - copy->set_text(TTR("Copy")); + copy->set_flat(true); + copy->set_tooltip(TTR("Copy")); hbc->add_child(copy); paste = memnew(Button); - paste->set_text(TTR("Paste")); + paste->set_flat(true); + paste->set_tooltip(TTR("Paste")); hbc->add_child(paste); empty = memnew(Button); - empty->set_text(TTR("Insert Empty (Before)")); + empty->set_flat(true); + empty->set_tooltip(TTR("Insert Empty (Before)")); hbc->add_child(empty); empty2 = memnew(Button); - empty2->set_text(TTR("Insert Empty (After)")); + empty2->set_flat(true); + empty2->set_tooltip(TTR("Insert Empty (After)")); hbc->add_child(empty2); + hbc->add_spacer(false); + move_up = memnew(Button); - move_up->set_text(TTR("Move (Before)")); + move_up->set_flat(true); + move_up->set_tooltip(TTR("Move (Before)")); hbc->add_child(move_up); move_down = memnew(Button); - move_down->set_text(TTR("Move (After)")); + move_down->set_flat(true); + move_down->set_tooltip(TTR("Move (After)")); hbc->add_child(move_down); _delete = memnew(Button); diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 36a578037e..e891850870 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -75,6 +75,9 @@ void TextureEditor::_notification(int p_what) { // In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient ofs_y = 0; tex_height = size.height; + } else if (Object::cast_to<GradientTexture>(*texture)) { + ofs_y = size.height / 4.0; + tex_height = size.height / 2.0; } draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height)); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index e04798d6d6..5ba3931689 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -57,6 +57,9 @@ void TextureRegionEditor::_region_draw() { base_tex = obj_styleBox->get_texture(); else if (atlas_tex.is_valid()) base_tex = atlas_tex->get_atlas(); + else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile)) + base_tex = tile_set->tile_get_texture(selected_tile); + if (base_tex.is_null()) return; @@ -281,6 +284,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { r = obj_styleBox->get_region_rect(); else if (atlas_tex.is_valid()) r = atlas_tex->get_region(); + else if (tile_set.is_valid() && selected_tile != -1) + r = tile_set->tile_get_region(selected_tile); rect.expand_to(r.position); rect.expand_to(r.position + r.size); } @@ -297,6 +302,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } else if (atlas_tex.is_valid()) { undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect); undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region()); + } else if (tile_set.is_valid() && selected_tile != -1) { + undo_redo->add_do_method(tile_set.ptr(), "tile_set_region", selected_tile, rect); + undo_redo->add_undo_method(tile_set.ptr(), "tile_set_region", selected_tile, tile_set->tile_get_region(selected_tile)); } undo_redo->add_do_method(edit_draw, "update"); undo_redo->add_undo_method(edit_draw, "update"); @@ -319,6 +327,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { rect_prev = obj_styleBox->get_region_rect(); else if (atlas_tex.is_valid()) rect_prev = atlas_tex->get_region(); + else if (tile_set.is_valid() && selected_tile != -1) + rect_prev = tile_set->tile_get_region(selected_tile); for (int i = 0; i < 8; i++) { Vector2 tuv = endpoints[i]; @@ -362,6 +372,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } else if (obj_styleBox.is_valid()) { undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect()); undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", rect_prev); + } else if (tile_set.is_valid()) { + undo_redo->add_do_method(tile_set.ptr(), "tile_set_region", selected_tile, tile_set->tile_get_region(selected_tile)); + undo_redo->add_undo_method(tile_set.ptr(), "tile_set_region", selected_tile, rect_prev); } drag_index = -1; } @@ -582,10 +595,13 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) { obj_styleBox->set_region_rect(rect); else if (atlas_tex.is_valid()) atlas_tex->set_region(rect); + else if (tile_set.is_valid() && selected_tile != -1) + tile_set->tile_set_region(selected_tile, rect); } void TextureRegionEditor::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_READY: { zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); @@ -596,11 +612,12 @@ void TextureRegionEditor::_notification(int p_what) { } void TextureRegionEditor::_node_removed(Object *p_obj) { - if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) { + if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr() || p_obj == tile_set.ptr()) { node_ninepatch = NULL; node_sprite = NULL; obj_styleBox = Ref<StyleBox>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); + tile_set = Ref<TileSet>(NULL); hide(); } } @@ -632,6 +649,8 @@ void TextureRegionEditor::edit(Object *p_obj) { obj_styleBox->remove_change_receptor(this); if (atlas_tex.is_valid()) atlas_tex->remove_change_receptor(this); + if (tile_set.is_valid()) + tile_set->remove_change_receptor(this); if (p_obj) { node_sprite = Object::cast_to<Sprite>(p_obj); node_ninepatch = Object::cast_to<NinePatchRect>(p_obj); @@ -639,6 +658,8 @@ void TextureRegionEditor::edit(Object *p_obj) { obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj)); if (Object::cast_to<AtlasTexture>(p_obj)) atlas_tex = Ref<AtlasTexture>(Object::cast_to<AtlasTexture>(p_obj)); + if (Object::cast_to<TileSet>(p_obj)) + tile_set = Ref<TileSet>(Object::cast_to<TileSet>(p_obj)); p_obj->add_change_receptor(this); _edit_region(); } else { @@ -646,6 +667,7 @@ void TextureRegionEditor::edit(Object *p_obj) { node_ninepatch = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); + tile_set = Ref<TileSet>(NULL); } edit_draw->update(); } @@ -668,8 +690,11 @@ void TextureRegionEditor::_edit_region() { texture = obj_styleBox->get_texture(); else if (atlas_tex.is_valid()) texture = atlas_tex->get_atlas(); + else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile)) + texture = tile_set->tile_get_texture(selected_tile); if (texture.is_null()) { + edit_draw->update(); return; } @@ -735,6 +760,8 @@ void TextureRegionEditor::_edit_region() { rect = obj_styleBox->get_region_rect(); else if (atlas_tex.is_valid()) rect = atlas_tex->get_region(); + else if (tile_set.is_valid() && selected_tile != -1) + rect = tile_set->tile_get_region(selected_tile); edit_draw->update(); } @@ -753,11 +780,14 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { node_ninepatch = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); + tile_set = Ref<TileSet>(NULL); editor = p_editor; undo_redo = editor->get_undo_redo(); + selected_tile = -1; snap_step = Vector2(10, 10); snap_separation = Vector2(0, 0); + snap_mode = SNAP_NONE; edited_margin = -1; drag_index = -1; drag = false; @@ -775,12 +805,10 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { snap_mode_button->set_text(TTR("<None>")); PopupMenu *p = snap_mode_button->get_popup(); p->set_hide_on_checkable_item_selection(false); - p->add_item(TTR("<None>"), 0); - p->add_item(TTR("Pixel Snap"), 1); - p->add_item(TTR("Grid Snap"), 2); - p->add_item(TTR("Auto Slice"), 3); - for (int i = 0; i < 4; i++) - p->set_item_as_checkable(i, true); + p->add_radio_check_item(TTR("<None>"), 0); + p->add_radio_check_item(TTR("Pixel Snap"), 1); + p->add_radio_check_item(TTR("Grid Snap"), 2); + p->add_radio_check_item(TTR("Auto Slice"), 3); p->set_item_checked(0, true); p->connect("id_pressed", this, "_set_snap_mode"); hb_grid = memnew(HBoxContainer); @@ -903,11 +931,11 @@ bool TextureRegionEditorPlugin::handles(Object *p_object) const { void TextureRegionEditorPlugin::make_visible(bool p_visible) { if (p_visible) { - region_button->show(); - if (region_button->is_pressed()) + texture_region_button->show(); + if (texture_region_button->is_pressed()) region_editor->show(); } else { - region_button->hide(); + texture_region_button->hide(); region_editor->edit(NULL); region_editor->hide(); } @@ -961,10 +989,10 @@ TextureRegionEditorPlugin::TextureRegionEditorPlugin(EditorNode *p_node) { editor = p_node; region_editor = memnew(TextureRegionEditor(p_node)); - region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor); - region_button->set_tooltip(TTR("Texture Region Editor")); + texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor); + texture_region_button->set_tooltip(TTR("Texture Region Editor")); region_editor->set_custom_minimum_size(Size2(0, 200)); region_editor->hide(); - region_button->hide(); + texture_region_button->hide(); } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index cf9396bd5b..1244953a3f 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -38,6 +38,7 @@ #include "scene/gui/nine_patch_rect.h" #include "scene/resources/style_box.h" #include "scene/resources/texture.h" +#include "scene/resources/tile_set.h" /** @author Mariano Suligoy @@ -55,6 +56,8 @@ class TextureRegionEditor : public Control { }; friend class TextureRegionEditorPlugin; + friend class TileSetEditor; + friend class TileSetEditorPlugin; MenuButton *snap_mode_button; TextureRect *icon_zoom; ToolButton *zoom_in; @@ -88,12 +91,14 @@ class TextureRegionEditor : public Control { Sprite *node_sprite; Ref<StyleBoxTexture> obj_styleBox; Ref<AtlasTexture> atlas_tex; + Ref<TileSet> tile_set; Rect2 rect; Rect2 rect_prev; float prev_margin; int edited_margin; List<Rect2> autoslice_cache; + int selected_tile; bool drag; bool creating; @@ -134,7 +139,7 @@ public: class TextureRegionEditorPlugin : public EditorPlugin { GDCLASS(TextureRegionEditorPlugin, EditorPlugin); - Button *region_button; + Button *texture_region_button; TextureRegionEditor *region_editor; EditorNode *editor; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index f51e691be3..92b95963f9 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -244,7 +244,7 @@ void ThemeEditor::_save_template_cbk(String fname) { file->store_line("; "); file->store_line("; ******************* "); file->store_line("; "); - file->store_line("; Template Generated Using: " + String(VERSION_MKSTRING)); + file->store_line("; Template Generated Using: " + String(VERSION_FULL_BUILD)); file->store_line("; "); file->store_line("; "); file->store_line(""); @@ -632,7 +632,7 @@ ThemeEditor::ThemeEditor() { main_vb->add_child(hb_menu); theme_menu = memnew(MenuButton); - theme_menu->set_text(TTR("Edit theme..")); + theme_menu->set_text(TTR("Edit theme...")); theme_menu->set_flat(false); theme_menu->set_tooltip(TTR("Theme editing menu.")); theme_menu->get_popup()->add_item(TTR("Add Item"), POPUP_ADD); @@ -691,7 +691,11 @@ ThemeEditor::ThemeEditor() { 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")); - test_menu_button->get_popup()->set_item_checked(2, true); + test_menu_button->get_popup()->set_item_checked(3, true); + test_menu_button->get_popup()->add_separator(); + 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); first_vb->add_child(test_menu_button); OptionButton *test_option_button = memnew(OptionButton); @@ -740,7 +744,7 @@ ThemeEditor::ThemeEditor() { 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->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); @@ -749,7 +753,7 @@ ThemeEditor::ThemeEditor() { 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("Have,Many,Several,Options!")); + item->set_text(0, TTR("Has,Many,Options")); item->set_range(0, 2); VBoxContainer *third_vb = memnew(VBoxContainer); @@ -780,58 +784,6 @@ ThemeEditor::ThemeEditor() { main_hb->add_constant_override("separation", 20 * EDSCALE); - /* - test_h_scroll = memnew( HScrollBar ); - test_h_scroll->set_position( Point2( 25, 225 ) ); - test_h_scroll->set_size( Point2( 150, 5 ) ); - panel->add_child(test_h_scroll); - - line_edit = memnew( LineEdit ); - line_edit->set_position( Point2( 25, 275 ) ); - line_edit->set_size( Point2( 150, 5 ) ); - line_edit->set_text("Line Edit"); - panel->add_child(line_edit); - - test_v_scroll = memnew( VScrollBar ); - test_v_scroll->set_position( Point2( 200, 25 ) ); - test_v_scroll->set_size( Point2( 5, 150 ) ); - panel->add_child(test_v_scroll); - - test_tree = memnew(Tree); - test_tree->set_position( Point2( 300, 25 ) ); - test_tree->set_size( Point2( 200, 200 ) ); - panel->add_child(test_tree); - - - TreeItem *item = test_tree->create_item(); - item->set_editable(0,true); - item->set_text(0,"root"); - 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,"Have,Many,Several,Options!")); - item->set_range(0,2); - - Button *fd_button= memnew( Button ); - fd_button->set_position(Point2(300,275)); - fd_button->set_text("Open File Dialog"); - panel->add_child(fd_button); - - test_file_dialog = memnew( EditorFileDialog ); - panel->add_child(test_file_dialog); - - fd_button->connect("pressed", this,"_open_file_dialog"); -*/ - add_del_dialog = memnew(ConfirmationDialog); add_del_dialog->hide(); add_child(add_del_dialog); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index a102d99d1c..72b3af5a09 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -48,6 +48,20 @@ void TileMapEditor::_notification(int p_what) { } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + + bool new_show_tile_info = EditorSettings::get_singleton()->get("editors/tile_map/show_tile_info_on_hover"); + if (new_show_tile_info != show_tile_info) { + show_tile_info = new_show_tile_info; + tile_info->set_visible(show_tile_info); + } + + if (is_visible_in_tree()) { + _update_palette(); + } + + } // fallthrough + case NOTIFICATION_ENTER_TREE: { transp->set_icon(get_icon("Transpose", "EditorIcons")); @@ -60,19 +74,14 @@ void TileMapEditor::_notification(int p_what) { search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - } break; - - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - - bool new_show_tile_info = EditorSettings::get_singleton()->get("editors/tile_map/show_tile_info_on_hover"); - if (new_show_tile_info != show_tile_info) { - show_tile_info = new_show_tile_info; - tile_info->set_visible(show_tile_info); - } + 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("ToolSelect", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_MOVE), get_icon("ToolMove", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_DUPLICATE), get_icon("Duplicate", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons")); - if (is_visible_in_tree()) { - _update_palette(); - } } break; } } @@ -139,6 +148,23 @@ void TileMapEditor::_menu_option(int p_option) { canvas_item_editor->update(); } break; + case OPTION_FIX_INVALID: { + + undo_redo->create_action(TTR("Fix Invalid Tiles")); + undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data")); + node->fix_invalid_tiles(); + undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data")); + undo_redo->commit_action(); + + } break; + case OPTION_MOVE: { + + if (selection_active) { + _update_copydata(); + tool = TOOL_MOVING; + canvas_item_editor->update(); + } + } break; } } @@ -303,7 +329,7 @@ void TileMapEditor::_update_palette() { if (tex.is_valid()) { Rect2 region = tileset->tile_get_region(entries[i].id); - if (tileset->tile_get_is_autotile(entries[i].id)) { + if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE) { int spacing = tileset->autotile_get_spacing(entries[i].id); region.size = tileset->autotile_get_size(entries[i].id); region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id); @@ -506,7 +532,7 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell); Rect2 r = node->get_tileset()->tile_get_region(p_cell); - if (node->get_tileset()->tile_get_is_autotile(p_cell)) { + if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE) { int spacing = node->get_tileset()->autotile_get_spacing(p_cell); r.size = node->get_tileset()->autotile_get_size(p_cell); r.position += (r.size + Vector2(spacing, spacing)) * node->get_tileset()->autotile_get_icon_coordinate(p_cell); @@ -698,7 +724,11 @@ 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; @@ -708,9 +738,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } - +#ifdef APPLE_STYLE_KEYS + if (mb->get_command()) { +#else if (mb->get_control()) { - +#endif tool = TOOL_PICKING; _pick_tile(over_tile); @@ -812,6 +844,29 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { copydata.clear(); canvas_item_editor->update(); + } else if (tool == TOOL_MOVING) { + + Point2 ofs = over_tile - rectangle.position; + + undo_redo->create_action(TTR("Move")); + undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data")); + for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) { + for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) { + + _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false); + } + } + for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) { + + _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose); + } + undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data")); + undo_redo->commit_action(); + + copydata.clear(); + selection_active = false; + + canvas_item_editor->update(); } else if (tool == TOOL_SELECTING) { @@ -871,6 +926,16 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } + if (tool == TOOL_MOVING) { + + tool = TOOL_NONE; + copydata.clear(); + + canvas_item_editor->update(); + + return true; + } + if (tool == TOOL_NONE) { paint_undo.clear(); @@ -881,8 +946,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data")); 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; @@ -1078,7 +1146,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (k->get_scancode() == KEY_ESCAPE) { - if (tool == TOOL_DUPLICATING) + if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) copydata.clear(); else if (tool == TOOL_SELECTING || selection_active) selection_active = false; @@ -1133,6 +1201,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } } + if (ED_IS_SHORTCUT("tile_map_editor/move_selection", p_event)) { + if (selection_active) { + _update_copydata(); + tool = TOOL_MOVING; + canvas_item_editor->update(); + return true; + } + } if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) { search_box->select_all(); search_box->grab_focus(); @@ -1142,18 +1218,21 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) { flip_h = !flip_h; mirror_x->set_pressed(flip_h); + _update_transform_buttons(); canvas_item_editor->update(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) { flip_v = !flip_v; mirror_y->set_pressed(flip_v); + _update_transform_buttons(); canvas_item_editor->update(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) { transpose = !transpose; transp->set_pressed(transpose); + _update_transform_buttons(); canvas_item_editor->update(); return true; } @@ -1326,7 +1405,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { _draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform); } } - } else if (tool == TOOL_DUPLICATING) { + } else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) { if (copydata.empty()) return; @@ -1511,8 +1590,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { bucket_cache_tile = -1; bucket_cache_visited = 0; - ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase selection"), KEY_DELETE); - ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find tile"), KEY_MASK_CMD + KEY_F); + ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE); + ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F); ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"), KEY_T); ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A); ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S); @@ -1573,8 +1652,11 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { 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/move_selection", TTR("Move Selection"), KEY_MASK_CMD + KEY_M), OPTION_MOVE); p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE); 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"); diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index 0a937e200e..642870aec0 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -61,6 +61,7 @@ class TileMapEditor : public VBoxContainer { TOOL_BUCKET, TOOL_PICKING, TOOL_DUPLICATING, + TOOL_MOVING }; enum Options { @@ -71,6 +72,8 @@ class TileMapEditor : public VBoxContainer { OPTION_DUPLICATE, OPTION_ERASE_SELECTION, OPTION_PAINTING, + OPTION_FIX_INVALID, + OPTION_MOVE }; TileMap *node; @@ -122,12 +125,11 @@ class TileMapEditor : public VBoxContainer { bool yf; bool tr; - CellOp() { - idx = -1; - xf = false; - yf = false; - tr = false; - } + CellOp() : + idx(TileMap::INVALID_CELL), + xf(false), + yf(false), + tr(false) {} }; Map<Point2i, CellOp> paint_undo; @@ -138,8 +140,12 @@ class TileMapEditor : public VBoxContainer { bool flip_h; bool flip_v; bool transpose; - int auto_x; - int auto_y; + + TileData() : + cell(TileMap::INVALID_CELL), + flip_h(false), + flip_v(false), + transpose(false) {} }; List<TileData> copydata; diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 1dedf92452..385fa24ad8 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -37,6 +37,9 @@ void TileSetEditor::edit(const Ref<TileSet> &p_tileset) { tileset = p_tileset; + tileset->add_change_receptor(this); + + update_tile_list(); } void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) { @@ -146,6 +149,7 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) { p_library->tile_set_light_occluder(id, occluder); p_library->tile_set_occluder_offset(id, -phys_offset); p_library->tile_set_navigation_polygon_offset(id, -phys_offset); + p_library->tile_set_z_index(id, mi->get_z_index()); } } @@ -188,6 +192,7 @@ void TileSetEditor::_name_dialog_confirm(const String &name) { if (tileset->has_tile(id)) { tileset->remove_tile(id); + update_tile_list(); } else { err_dialog->set_text(TTR("Could not find tile:") + " " + name); err_dialog->popup_centered(Size2(300, 60)); @@ -202,8 +207,9 @@ void TileSetEditor::_menu_cbk(int p_option) { switch (p_option) { case MENU_OPTION_ADD_ITEM: { - tileset->create_tile(tileset->get_last_unused_tile_id()); + tileset->tile_set_name(tileset->get_last_unused_tile_id() - 1, itos(tileset->get_last_unused_tile_id() - 1)); + update_tile_list(); } break; case MENU_OPTION_REMOVE_ITEM: { @@ -235,106 +241,77 @@ void TileSetEditor::_bind_methods() { ClassDB::bind_method("_menu_cbk", &TileSetEditor::_menu_cbk); ClassDB::bind_method("_menu_confirm", &TileSetEditor::_menu_confirm); ClassDB::bind_method("_name_dialog_confirm", &TileSetEditor::_name_dialog_confirm); + ClassDB::bind_method("_on_tile_list_selected", &TileSetEditor::_on_tile_list_selected); + ClassDB::bind_method("_on_edit_mode_changed", &TileSetEditor::_on_edit_mode_changed); + ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw); + ClassDB::bind_method("_on_workspace_draw", &TileSetEditor::_on_workspace_draw); + ClassDB::bind_method("_on_workspace_input", &TileSetEditor::_on_workspace_input); + ClassDB::bind_method("_on_tool_clicked", &TileSetEditor::_on_tool_clicked); + ClassDB::bind_method("_on_priority_changed", &TileSetEditor::_on_priority_changed); + ClassDB::bind_method("_on_grid_snap_toggled", &TileSetEditor::_on_grid_snap_toggled); + ClassDB::bind_method("_set_snap_step_x", &TileSetEditor::_set_snap_step_x); + ClassDB::bind_method("_set_snap_step_y", &TileSetEditor::_set_snap_step_y); + ClassDB::bind_method("_set_snap_off_x", &TileSetEditor::_set_snap_off_x); + ClassDB::bind_method("_set_snap_off_y", &TileSetEditor::_set_snap_off_y); + ClassDB::bind_method("_set_snap_sep_x", &TileSetEditor::_set_snap_sep_x); + ClassDB::bind_method("_set_snap_sep_y", &TileSetEditor::_set_snap_sep_y); } -TileSetEditor::TileSetEditor(EditorNode *p_editor) { - - menu = memnew(MenuButton); - CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu); - menu->hide(); - menu->set_text(TTR("Tile Set")); - menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); - menu->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM); - menu->get_popup()->add_separator(); - menu->get_popup()->add_item(TTR("Create from Scene"), MENU_OPTION_CREATE_FROM_SCENE); - menu->get_popup()->add_item(TTR("Merge from Scene"), MENU_OPTION_MERGE_FROM_SCENE); - menu->get_popup()->connect("id_pressed", this, "_menu_cbk"); - editor = p_editor; - cd = memnew(ConfirmationDialog); - add_child(cd); - cd->get_ok()->connect("pressed", this, "_menu_confirm"); - - nd = memnew(EditorNameDialog); - add_child(nd); - nd->set_hide_on_ok(true); - nd->get_line_edit()->set_margin(MARGIN_TOP, 28); - nd->connect("name_confirmed", this, "_name_dialog_confirm"); - - err_dialog = memnew(AcceptDialog); - add_child(err_dialog); - err_dialog->set_title(TTR("Error")); -} - -void TileSetEditorPlugin::edit(Object *p_node) { - - if (Object::cast_to<TileSet>(p_node)) { - tileset_editor->edit(Object::cast_to<TileSet>(p_node)); - tileset_editor->show(); - autotile_editor->edit(p_node); - } else - tileset_editor->hide(); -} - -bool TileSetEditorPlugin::handles(Object *p_node) const { - - return p_node->is_class("TileSet"); -} - -void TileSetEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - tileset_editor->show(); - tileset_editor->menu->show(); - autotile_button->show(); - autotile_editor->side_panel->show(); - if (autotile_button->is_pressed()) { - autotile_editor->show(); - } - } else { - tileset_editor->hide(); - tileset_editor->menu->hide(); - autotile_editor->side_panel->hide(); - autotile_editor->hide(); - autotile_button->hide(); +void TileSetEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); + tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons")); + 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_DELETE]->set_icon(get_icon("Remove", "EditorIcons")); + tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons")); + tools[SHAPE_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); + tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons")); + tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons")); + tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons")); } } -TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) { - - tileset_editor = memnew(TileSetEditor(p_node)); - - add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, tileset_editor); - tileset_editor->set_anchors_and_margins_preset(Control::PRESET_TOP_WIDE); - tileset_editor->set_end(Point2(0, 22)); - tileset_editor->hide(); - - autotile_editor = memnew(AutotileEditor(p_node)); - add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, autotile_editor->side_panel); - autotile_editor->side_panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - autotile_editor->side_panel->set_custom_minimum_size(Size2(200, 0)); - autotile_editor->side_panel->hide(); - autotile_button = p_node->add_bottom_panel_item(TTR("Autotiles"), autotile_editor); - autotile_button->hide(); +void TileSetEditor::_changed_callback(Object *p_changed, const char *p_prop) { + if (p_prop == StringName("region")) { + update_tile_list_icon(); + preview->set_region_rect(tileset->tile_get_region(get_current_tile())); + } else if (p_prop == StringName("name")) { + update_tile_list_icon(); + } else if (p_prop == StringName("texture") || p_prop == StringName("modulate") || p_prop == StringName("tile_mode")) { + _on_tile_list_selected(get_current_tile()); + workspace->update(); + preview->set_texture(tileset->tile_get_texture(get_current_tile())); + preview->set_modulate(tileset->tile_get_modulate(get_current_tile())); + preview->set_region_rect(tileset->tile_get_region(get_current_tile())); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + property_editor->show(); + else + property_editor->hide(); + texture_region_editor->_edit_region(); + update_tile_list_icon(); + } else if (p_prop == StringName("autotile")) { + workspace->update(); + } } -AutotileEditor::AutotileEditor(EditorNode *p_editor) { - - editor = p_editor; +void TileSetEditor::initialize_bottom_editor() { //Side Panel side_panel = memnew(Control); - side_panel->set_name("Autotiles"); + side_panel->set_name("Tile Set"); VSplitContainer *split = memnew(VSplitContainer); side_panel->add_child(split); split->set_anchors_and_margins_preset(Control::PRESET_WIDE); - autotile_list = memnew(ItemList); - autotile_list->set_v_size_flags(SIZE_EXPAND_FILL); - autotile_list->set_h_size_flags(SIZE_EXPAND_FILL); - autotile_list->set_custom_minimum_size(Size2(10, 200)); - autotile_list->connect("item_selected", this, "_on_autotile_selected"); - split->add_child(autotile_list); + tile_list = memnew(ItemList); + tile_list->set_v_size_flags(SIZE_EXPAND_FILL); + tile_list->set_h_size_flags(SIZE_EXPAND_FILL); + tile_list->set_custom_minimum_size(Size2(10, 200)); + tile_list->connect("item_selected", this, "_on_tile_list_selected"); + split->add_child(tile_list); property_editor = memnew(PropertyEditor); property_editor->set_v_size_flags(SIZE_EXPAND_FILL); @@ -342,25 +319,29 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { property_editor->set_custom_minimum_size(Size2(10, 70)); split->add_child(property_editor); - helper = memnew(AutotileEditorHelper(this)); + helper = memnew(TileSetEditorHelper(this)); property_editor->call_deferred("edit", helper); + helper->add_change_receptor(this); //Editor + //Bottom Panel + bottom_panel = memnew(Control); + bottom_panel->set_name("Tile Set Bottom Editor"); dragging_point = -1; creating_shape = false; snap_step = Vector2(32, 32); - set_custom_minimum_size(Size2(0, 150)); + bottom_panel->set_custom_minimum_size(Size2(0, 150)); VBoxContainer *main_vb = memnew(VBoxContainer); - add_child(main_vb); + bottom_panel->add_child(main_vb); main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE); HBoxContainer *tool_hb = memnew(HBoxContainer); Ref<ButtonGroup> g(memnew(ButtonGroup)); - String label[EDITMODE_MAX] = { "Icon", "Bitmask", "Collision", "Occlusion", "Navigation", "Priority" }; + String label[EDITMODE_MAX] = { "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon" }; for (int i = 0; i < (int)EDITMODE_MAX; i++) { tool_editmode[i] = memnew(Button); @@ -372,7 +353,8 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { tool_editmode[i]->connect("pressed", this, "_on_edit_mode_changed", args); tool_hb->add_child(tool_editmode[i]); } - tool_editmode[EDITMODE_ICON]->set_pressed(true); + tool_editmode[EDITMODE_COLLISION]->set_pressed(true); + edit_mode = EDITMODE_COLLISION; main_vb->add_child(tool_hb); main_vb->add_child(memnew(HSeparator)); @@ -386,15 +368,17 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { Ref<ButtonGroup> tg(memnew(ButtonGroup)); + Vector<Variant> p; tools[TOOL_SELECT] = memnew(ToolButton); tool_containers[TOOLBAR_DUMMY]->add_child(tools[TOOL_SELECT]); tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.")); tools[TOOL_SELECT]->set_toggle_mode(true); tools[TOOL_SELECT]->set_button_group(tg); tools[TOOL_SELECT]->set_pressed(true); + p.push_back((int)TOOL_SELECT); + tools[TOOL_SELECT]->connect("pressed", this, "_on_tool_clicked", p); tool_containers[TOOLBAR_DUMMY]->show(); - Vector<Variant> p; tools[BITMASK_COPY] = memnew(ToolButton); p.push_back((int)BITMASK_COPY); tools[BITMASK_COPY]->connect("pressed", this, "_on_tool_clicked", p); @@ -420,7 +404,7 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { p.push_back((int)SHAPE_DELETE); tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", p); tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_DELETE]); - tool_containers[TOOLBAR_SHAPE]->add_change_receptor(memnew(VSeparator)); + tool_containers[TOOLBAR_SHAPE]->add_child(memnew(VSeparator)); tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton); tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true); @@ -507,6 +491,8 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { spin_priority->hide(); toolbar->add_child(spin_priority); + tool_containers[TOOLBAR_SHAPE]->show(); + Control *separator = memnew(Control); separator->set_h_size_flags(SIZE_EXPAND_FILL); toolbar->add_child(separator); @@ -536,10 +522,15 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { workspace_container = memnew(Control); scroll->add_child(workspace_container); + workspace_overlay = memnew(Control); + workspace_overlay->connect("draw", this, "_on_workspace_overlay_draw"); + workspace_container->add_child(workspace_overlay); + workspace = memnew(Control); workspace->connect("draw", this, "_on_workspace_draw"); workspace->connect("gui_input", this, "_on_workspace_input"); - workspace_container->add_child(workspace); + workspace->set_draw_behind_parent(true); + workspace_overlay->add_child(workspace); preview = memnew(Sprite); workspace->add_child(preview); @@ -548,66 +539,64 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) { preview->set_region(true); } -void AutotileEditor::_bind_methods() { - - ClassDB::bind_method("_on_autotile_selected", &AutotileEditor::_on_autotile_selected); - ClassDB::bind_method("_on_edit_mode_changed", &AutotileEditor::_on_edit_mode_changed); - ClassDB::bind_method("_on_workspace_draw", &AutotileEditor::_on_workspace_draw); - ClassDB::bind_method("_on_workspace_input", &AutotileEditor::_on_workspace_input); - ClassDB::bind_method("_on_tool_clicked", &AutotileEditor::_on_tool_clicked); - ClassDB::bind_method("_on_priority_changed", &AutotileEditor::_on_priority_changed); - ClassDB::bind_method("_on_grid_snap_toggled", &AutotileEditor::_on_grid_snap_toggled); - ClassDB::bind_method("_set_snap_step_x", &AutotileEditor::_set_snap_step_x); - ClassDB::bind_method("_set_snap_step_y", &AutotileEditor::_set_snap_step_y); - ClassDB::bind_method("_set_snap_off_x", &AutotileEditor::_set_snap_off_x); - ClassDB::bind_method("_set_snap_off_y", &AutotileEditor::_set_snap_off_y); - ClassDB::bind_method("_set_snap_sep_x", &AutotileEditor::_set_snap_sep_x); - ClassDB::bind_method("_set_snap_sep_y", &AutotileEditor::_set_snap_sep_y); -} +TileSetEditor::TileSetEditor(EditorNode *p_editor) { -void AutotileEditor::_notification(int p_what) { + menu = memnew(MenuButton); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu); + menu->hide(); + menu->set_text(TTR("Tile Set")); + menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); + menu->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Create from Scene"), MENU_OPTION_CREATE_FROM_SCENE); + menu->get_popup()->add_item(TTR("Merge from Scene"), MENU_OPTION_MERGE_FROM_SCENE); + menu->get_popup()->connect("id_pressed", this, "_menu_cbk"); + editor = p_editor; + cd = memnew(ConfirmationDialog); + add_child(cd); + cd->get_ok()->connect("pressed", this, "_menu_confirm"); - if (p_what == NOTIFICATION_ENTER_TREE) { - tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); - tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons")); - 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_DELETE]->set_icon(get_icon("Remove", "EditorIcons")); - tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons")); - tools[SHAPE_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); - tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons")); - tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons")); - tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons")); - } -} + nd = memnew(EditorNameDialog); + add_child(nd); + nd->set_hide_on_ok(true); + nd->get_line_edit()->set_margin(MARGIN_TOP, 28); + nd->connect("name_confirmed", this, "_name_dialog_confirm"); -void AutotileEditor::_changed_callback(Object *p_changed, const char *p_prop) { - if (p_prop == StringName("texture") || p_prop == StringName("is_autotile")) { - edit(tile_set.ptr()); - autotile_list->update(); - workspace->update(); - } + err_dialog = memnew(AcceptDialog); + add_child(err_dialog); + err_dialog->set_title(TTR("Error")); + + draw_handles = false; + + initialize_bottom_editor(); } -void AutotileEditor::_on_autotile_selected(int p_index) { +TileSetEditor::~TileSetEditor() { + if (helper) + memdelete(helper); +} +void TileSetEditor::_on_tile_list_selected(int p_index) { if (get_current_tile() >= 0) { current_item_index = p_index; - preview->set_texture(tile_set->tile_get_texture(get_current_tile())); - preview->set_region_rect(tile_set->tile_get_region(get_current_tile())); - workspace->set_custom_minimum_size(tile_set->tile_get_region(get_current_tile()).size); + preview->set_texture(tileset->tile_get_texture(get_current_tile())); + preview->set_modulate(tileset->tile_get_modulate(get_current_tile())); + preview->set_region_rect(tileset->tile_get_region(get_current_tile())); + workspace->set_custom_minimum_size(tileset->tile_get_region(get_current_tile()).size); + update_workspace_tile_mode(); } else { current_item_index = -1; preview->set_texture(NULL); workspace->set_custom_minimum_size(Size2i()); } + texture_region_editor->selected_tile = get_current_tile(); + texture_region_editor->_edit_region(); + helper->selected_tile = get_current_tile(); helper->_change_notify(""); workspace->update(); } -void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) { - +void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { edit_mode = (EditMode)p_edit_mode; switch (edit_mode) { case EDITMODE_BITMASK: { @@ -627,7 +616,6 @@ void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) { tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile.")); spin_priority->hide(); - current_shape = PoolVector2Array(); select_coord(edited_shape_coord); } break; default: { @@ -646,17 +634,17 @@ void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) { workspace->update(); } -void AutotileEditor::_on_workspace_draw() { +void TileSetEditor::_on_workspace_draw() { - if (get_current_tile() >= 0 && !tile_set.is_null()) { - int spacing = tile_set->autotile_get_spacing(get_current_tile()); - Vector2 size = tile_set->autotile_get_size(get_current_tile()); - Rect2i region = tile_set->tile_get_region(get_current_tile()); + if (get_current_tile() >= 0 && !tileset.is_null()) { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + Rect2i region = tileset->tile_get_region(get_current_tile()); Color c(0.347214, 0.722656, 0.617063); switch (edit_mode) { case EDITMODE_ICON: { - Vector2 coord = tile_set->autotile_get_icon_coordinate(get_current_tile()); + Vector2 coord = tileset->autotile_get_icon_coordinate(get_current_tile()); draw_highlight_tile(coord); } break; case EDITMODE_BITMASK: { @@ -665,8 +653,8 @@ void AutotileEditor::_on_workspace_draw() { 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)); - uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord); - if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { + uint16_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) { workspace->draw_rect(Rect2(anchor, size / 2), c); } @@ -679,7 +667,7 @@ void AutotileEditor::_on_workspace_draw() { if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c); } - } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { + } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 3), c); } @@ -714,19 +702,21 @@ void AutotileEditor::_on_workspace_draw() { case EDITMODE_COLLISION: case EDITMODE_OCCLUSION: case EDITMODE_NAVIGATION: { - Vector2 coord = edited_shape_coord; - draw_highlight_tile(coord); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + Vector2 coord = edited_shape_coord; + draw_highlight_tile(coord); + } draw_polygon_shapes(); draw_grid_snap(); } break; case EDITMODE_PRIORITY: { - spin_priority->set_value(tile_set->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)); - uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), edited_shape_coord); + 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); Vector<Vector2> queue_others; int total = 0; - for (Map<Vector2, uint16_t>::Element *E = tile_set->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { if (E->value() == mask) { - total += tile_set->autotile_get_subtile_priority(get_current_tile(), E->key()); + total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key()); if (E->key() != edited_shape_coord) { queue_others.push_back(E->key()); } @@ -737,53 +727,69 @@ void AutotileEditor::_on_workspace_draw() { } break; } - float j = -size.x; //make sure to draw at 0 - while (j < region.size.x) { - j += size.x; - if (spacing <= 0) { - workspace->draw_line(Point2(j, 0), Point2(j, region.size.y), c); - } else { - workspace->draw_rect(Rect2(Point2(j, 0), Size2(spacing, region.size.y)), c); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + float j = -size.x; //make sure to draw at 0 + while (j < region.size.x) { + j += size.x; + if (spacing <= 0) { + workspace->draw_line(Point2(j, 0), Point2(j, region.size.y), c); + } else { + workspace->draw_rect(Rect2(Point2(j, 0), Size2(spacing, region.size.y)), c); + } + j += spacing; } - j += spacing; - } - j = -size.y; //make sure to draw at 0 - while (j < region.size.y) { - j += size.y; - if (spacing <= 0) { - workspace->draw_line(Point2(0, j), Point2(region.size.x, j), c); - } else { - workspace->draw_rect(Rect2(Point2(0, j), Size2(region.size.x, spacing)), c); + j = -size.y; //make sure to draw at 0 + while (j < region.size.y) { + j += size.y; + if (spacing <= 0) { + workspace->draw_line(Point2(0, j), Point2(region.size.x, j), c); + } else { + workspace->draw_rect(Rect2(Point2(0, j), Size2(region.size.x, spacing)), c); + } + j += spacing; } - j += spacing; } } + workspace_overlay->update(); +} + +void TileSetEditor::_on_workspace_overlay_draw() { + + int t_id = get_current_tile(); + if (t_id < 0 || !draw_handles) + return; + + Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); + + for (int i = 0; i < current_shape.size(); i++) { + workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5); + } } -#define MIN_DISTANCE_SQUARED 10 -void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { +#define MIN_DISTANCE_SQUARED 6 +void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { - if (get_current_tile() >= 0 && !tile_set.is_null()) { + if (get_current_tile() >= 0 && !tileset.is_null()) { Ref<InputEventMouseButton> mb = p_ie; Ref<InputEventMouseMotion> mm = p_ie; static bool dragging; static bool erasing; - int spacing = tile_set->autotile_get_spacing(get_current_tile()); - Vector2 size = tile_set->autotile_get_size(get_current_tile()); + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); switch (edit_mode) { case EDITMODE_ICON: { if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); - tile_set->autotile_set_icon_coordinate(get_current_tile(), coord); - Rect2 region = tile_set->tile_get_region(get_current_tile()); + tileset->autotile_set_icon_coordinate(get_current_tile(), coord); + Rect2 region = tileset->tile_get_region(get_current_tile()); region.size = size; coord.x *= (spacing + size.x); coord.y *= (spacing + size.y); region.position += coord; - autotile_list->set_item_icon_region(current_item_index, region); + tile_list->set_item_icon_region(current_item_index, region); workspace->update(); } } @@ -800,8 +806,8 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mb->get_position() - pos; - uint16_t bit; - if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { + uint16_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) { bit = TileSet::BIND_TOPLEFT; @@ -815,7 +821,7 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { bit = TileSet::BIND_BOTTOMRIGHT; } } - } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { + } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { if (pos.x < size.x / 3) { if (pos.y < size.y / 3) { bit = TileSet::BIND_TOPLEFT; @@ -842,13 +848,13 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } - uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord); + uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); if (erasing) { mask &= ~bit; } else { mask |= bit; } - tile_set->autotile_set_bitmask(get_current_tile(), coord, mask); + tileset->autotile_set_bitmask(get_current_tile(), coord, mask); workspace->update(); } } else { @@ -863,8 +869,8 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)(mm->get_position().x / (spacing + size.x)), (int)(mm->get_position().y / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mm->get_position() - pos; - uint16_t bit; - if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { + uint16_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) { bit = TileSet::BIND_TOPLEFT; @@ -878,7 +884,7 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { bit = TileSet::BIND_BOTTOMRIGHT; } } - } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { + } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) { if (pos.x < size.x / 3) { if (pos.y < size.y / 3) { bit = TileSet::BIND_TOPLEFT; @@ -905,13 +911,13 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } - uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord); + uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); if (erasing) { mask &= ~bit; } else { mask |= bit; } - tile_set->autotile_set_bitmask(get_current_tile(), coord, mask); + tileset->autotile_set_bitmask(get_current_tile(), coord, mask); workspace->update(); } } @@ -920,9 +926,12 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { case EDITMODE_OCCLUSION: case EDITMODE_NAVIGATION: case EDITMODE_PRIORITY: { - Vector2 shape_anchor = edited_shape_coord; - shape_anchor.x *= (size.x + spacing); - shape_anchor.y *= (size.y + spacing); + Vector2 shape_anchor = Vector2(0, 0); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + shape_anchor = edited_shape_coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); + } if (tools[TOOL_SELECT]->is_pressed()) { if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { @@ -935,23 +944,25 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } - Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); - if (edited_shape_coord != coord) { - edited_shape_coord = coord; - edited_occlusion_shape = tile_set->autotile_get_light_occluder(get_current_tile(), edited_shape_coord); - edited_navigation_shape = tile_set->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord); - Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile()); - bool found_collision_shape = false; - for (int i = 0; i < sd.size(); i++) { - if (sd[i].autotile_coord == coord) { - edited_collision_shape = sd[i].shape; - found_collision_shape = true; - break; + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); + if (edited_shape_coord != coord) { + edited_shape_coord = coord; + edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord); + edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord); + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); + bool found_collision_shape = false; + for (int i = 0; i < sd.size(); i++) { + if (sd[i].autotile_coord == coord) { + edited_collision_shape = sd[i].shape; + found_collision_shape = true; + break; + } } + if (!found_collision_shape) + edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL); + select_coord(edited_shape_coord); } - if (!found_collision_shape) - edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL); - select_coord(edited_shape_coord); } workspace->update(); } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { @@ -1019,6 +1030,7 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) { + if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { Vector2 pos = mb->get_position(); @@ -1039,14 +1051,14 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { int t_id = get_current_tile(); if (t_id >= 0) { if (edit_mode == EDITMODE_COLLISION) { - Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(t_id); + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id); for (int i = 0; i < sd.size(); i++) { - if (sd[i].autotile_coord == edited_shape_coord) { + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || sd[i].autotile_coord == edited_shape_coord) { Ref<ConvexPolygonShape2D> shape = sd[i].shape; if (!shape.is_null()) { sd.remove(i); - tile_set->tile_set_shapes(get_current_tile(), sd); + tileset->tile_set_shapes(get_current_tile(), sd); edited_collision_shape = Ref<Shape2D>(); workspace->update(); } @@ -1054,25 +1066,30 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } else if (edit_mode == EDITMODE_OCCLUSION) { - Map<Vector2, Ref<OccluderPolygon2D> > map = tile_set->autotile_get_light_oclusion_map(t_id); - for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { - if (E->key() == edited_shape_coord) { - tile_set->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); - break; + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id); + for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { + if (E->key() == edited_shape_coord) { + tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); + break; + } } - } + } else + tileset->tile_set_light_occluder(t_id, Ref<OccluderPolygon2D>()); edited_occlusion_shape = Ref<OccluderPolygon2D>(); workspace->update(); } else if (edit_mode == EDITMODE_NAVIGATION) { - Map<Vector2, Ref<NavigationPolygon> > map = tile_set->autotile_get_navigation_map(t_id); - for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { - if (E->key() == edited_shape_coord) { - tile_set->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord); - break; + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id); + for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { + if (E->key() == edited_shape_coord) { + tileset->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord); + break; + } } - } - + } else + tileset->tile_set_navigation_polygon(t_id, Ref<NavigationPolygon>()); edited_navigation_shape = Ref<NavigationPolygon>(); workspace->update(); } @@ -1108,17 +1125,17 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } -void AutotileEditor::_on_tool_clicked(int p_tool) { +void TileSetEditor::_on_tool_clicked(int p_tool) { if (p_tool == BITMASK_COPY) { - bitmask_map_copy = tile_set->autotile_get_bitmask_map(get_current_tile()); + bitmask_map_copy = tileset->autotile_get_bitmask_map(get_current_tile()); } else if (p_tool == BITMASK_PASTE) { - tile_set->autotile_clear_bitmask_map(get_current_tile()); + tileset->autotile_clear_bitmask_map(get_current_tile()); for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { - tile_set->autotile_set_bitmask(get_current_tile(), E->key(), E->value()); + tileset->autotile_set_bitmask(get_current_tile(), E->key(), E->value()); } workspace->update(); } else if (p_tool == BITMASK_CLEAR) { - tile_set->autotile_clear_bitmask_map(get_current_tile()); + tileset->autotile_clear_bitmask_map(get_current_tile()); workspace->update(); } else if (p_tool == SHAPE_DELETE) { if (creating_shape) { @@ -1129,8 +1146,8 @@ void AutotileEditor::_on_tool_clicked(int p_tool) { switch (edit_mode) { case EDITMODE_COLLISION: { if (!edited_collision_shape.is_null()) { - Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile()); - int index; + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); + int index = -1; for (int i = 0; i < sd.size(); i++) { if (sd[i].shape == edited_collision_shape) { index = i; @@ -1139,7 +1156,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) { } if (index >= 0) { sd.remove(index); - tile_set->tile_set_shapes(get_current_tile(), sd); + tileset->tile_set_shapes(get_current_tile(), sd); edited_collision_shape = Ref<Shape2D>(); current_shape.resize(0); workspace->update(); @@ -1148,7 +1165,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) { } break; case EDITMODE_NAVIGATION: { if (!edited_navigation_shape.is_null()) { - tile_set->autotile_set_navigation_polygon(get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord); + tileset->autotile_set_navigation_polygon(get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord); edited_navigation_shape = Ref<NavigationPolygon>(); current_shape.resize(0); workspace->update(); @@ -1156,7 +1173,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) { } break; case EDITMODE_OCCLUSION: { if (!edited_occlusion_shape.is_null()) { - tile_set->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); + tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); edited_occlusion_shape = Ref<OccluderPolygon2D>(); current_shape.resize(0); workspace->update(); @@ -1170,24 +1187,34 @@ void AutotileEditor::_on_tool_clicked(int p_tool) { scale /= 2; workspace->set_scale(Vector2(scale, scale)); workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale); + workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale); } } else if (p_tool == ZOOM_1) { workspace->set_scale(Vector2(1, 1)); workspace_container->set_custom_minimum_size(preview->get_region_rect().size); + workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size); } else if (p_tool == ZOOM_IN) { float scale = workspace->get_scale().x; scale *= 2; workspace->set_scale(Vector2(scale, scale)); workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale); + workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale); + } else if (p_tool == TOOL_SELECT) { + if (creating_shape) { + //Cancel Creation + creating_shape = false; + current_shape.resize(0); + workspace->update(); + } } } -void AutotileEditor::_on_priority_changed(float val) { - tile_set->autotile_set_subtile_priority(get_current_tile(), edited_shape_coord, (int)val); +void TileSetEditor::_on_priority_changed(float val) { + tileset->autotile_set_subtile_priority(get_current_tile(), edited_shape_coord, (int)val); workspace->update(); } -void AutotileEditor::_on_grid_snap_toggled(bool p_val) { +void TileSetEditor::_on_grid_snap_toggled(bool p_val) { if (p_val) hb_grid->show(); else @@ -1195,40 +1222,40 @@ void AutotileEditor::_on_grid_snap_toggled(bool p_val) { workspace->update(); } -void AutotileEditor::_set_snap_step_x(float p_val) { +void TileSetEditor::_set_snap_step_x(float p_val) { snap_step.x = p_val; workspace->update(); } -void AutotileEditor::_set_snap_step_y(float p_val) { +void TileSetEditor::_set_snap_step_y(float p_val) { snap_step.y = p_val; workspace->update(); } -void AutotileEditor::_set_snap_off_x(float p_val) { +void TileSetEditor::_set_snap_off_x(float p_val) { snap_offset.x = p_val; workspace->update(); } -void AutotileEditor::_set_snap_off_y(float p_val) { +void TileSetEditor::_set_snap_off_y(float p_val) { snap_offset.y = p_val; workspace->update(); } -void AutotileEditor::_set_snap_sep_x(float p_val) { +void TileSetEditor::_set_snap_sep_x(float p_val) { snap_separation.x = p_val; workspace->update(); } -void AutotileEditor::_set_snap_sep_y(float p_val) { +void TileSetEditor::_set_snap_sep_y(float p_val) { snap_separation.y = p_val; workspace->update(); } -void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) { +void TileSetEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) { - Vector2 size = tile_set->autotile_get_size(get_current_tile()); - int spacing = tile_set->autotile_get_spacing(get_current_tile()); - Rect2 region = tile_set->tile_get_region(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Rect2 region = tileset->tile_get_region(get_current_tile()); coord.x *= (size.x + spacing); coord.y *= (size.y + spacing); workspace->draw_rect(Rect2(0, 0, region.size.x, coord.y), Color(0.5, 0.5, 0.5, 0.5)); @@ -1246,7 +1273,7 @@ void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &o } } -void AutotileEditor::draw_grid_snap() { +void TileSetEditor::draw_grid_snap() { if (tools[SHAPE_GRID_SNAP]->is_pressed()) { Color grid_color = Color(0.39, 0, 1, 0.2f); Size2 s = workspace->get_size(); @@ -1287,27 +1314,33 @@ void AutotileEditor::draw_grid_snap() { } } -void AutotileEditor::draw_polygon_shapes() { +void TileSetEditor::draw_polygon_shapes() { int t_id = get_current_tile(); if (t_id < 0) return; + draw_handles = false; + switch (edit_mode) { case EDITMODE_COLLISION: { - Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(t_id); + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id); for (int i = 0; i < sd.size(); i++) { - Vector2 coord = sd[i].autotile_coord; - Vector2 anchor = tile_set->autotile_get_size(t_id); - anchor.x += tile_set->autotile_get_spacing(t_id); - anchor.y += tile_set->autotile_get_spacing(t_id); - anchor.x *= coord.x; - anchor.y *= coord.y; + Vector2 coord = Vector2(0, 0); + Vector2 anchor = Vector2(0, 0); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + coord = sd[i].autotile_coord; + anchor = tileset->autotile_get_size(t_id); + anchor.x += tileset->autotile_get_spacing(t_id); + anchor.y += tileset->autotile_get_spacing(t_id); + anchor.x *= coord.x; + anchor.y *= coord.y; + } Ref<ConvexPolygonShape2D> shape = sd[i].shape; if (shape.is_valid()) { Color c_bg; Color c_border; - if (coord == edited_shape_coord && sd[i].shape == edited_collision_shape) { + 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); } else { @@ -1330,62 +1363,82 @@ void AutotileEditor::draw_polygon_shapes() { if (polygon.size() > 2) { workspace->draw_polygon(polygon, colors); } - if (coord == edited_shape_coord) { + if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { for (int j = 0; j < shape->get_points().size() - 1; j++) { workspace->draw_line(shape->get_points()[j] + anchor, shape->get_points()[j + 1] + anchor, c_border, 1, true); } if (shape == edited_collision_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0, 0.7f)); - } + draw_handles = true; } } } } } break; case EDITMODE_OCCLUSION: { - Map<Vector2, Ref<OccluderPolygon2D> > map = tile_set->autotile_get_light_oclusion_map(t_id); - for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { - Vector2 coord = E->key(); - Vector2 anchor = tile_set->autotile_get_size(t_id); - anchor.x += tile_set->autotile_get_spacing(t_id); - anchor.y += tile_set->autotile_get_spacing(t_id); - anchor.x *= coord.x; - anchor.y *= coord.y; - Ref<OccluderPolygon2D> shape = E->value(); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + Ref<OccluderPolygon2D> shape = edited_occlusion_shape; if (shape.is_valid()) { - Color c_bg; - Color c_border; - if (coord == edited_shape_coord && shape == edited_occlusion_shape) { - c_bg = Color(0, 1, 1, 0.5); - c_border = Color(0, 1, 1); - } else { - c_bg = Color(0.9, 0.7, 0.07, 0.5); - c_border = Color(0.9, 0.7, 0.07, 1); - } + Color c_bg = Color(0, 1, 1, 0.5); + Color c_border = Color(0, 1, 1); + Vector<Vector2> polygon; Vector<Color> colors; - if (shape == edited_occlusion_shape && current_shape.size() > 2) { - for (int j = 0; j < current_shape.size(); j++) { - polygon.push_back(current_shape[j]); - colors.push_back(c_bg); - } - } else { - for (int j = 0; j < shape->get_polygon().size(); j++) { - polygon.push_back(shape->get_polygon()[j] + anchor); - colors.push_back(c_bg); - } + for (int j = 0; j < shape->get_polygon().size(); j++) { + polygon.push_back(shape->get_polygon()[j]); + colors.push_back(c_bg); } workspace->draw_polygon(polygon, colors); - if (coord == edited_shape_coord) { - for (int j = 0; j < shape->get_polygon().size() - 1; j++) { - workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true); + + for (int j = 0; j < shape->get_polygon().size() - 1; j++) { + workspace->draw_line(shape->get_polygon()[j], shape->get_polygon()[j + 1], c_border, 1, true); + } + workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1], shape->get_polygon()[0], c_border, 1, true); + if (shape == edited_occlusion_shape) { + draw_handles = true; + } + } + } else { + Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id); + for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { + Vector2 coord = E->key(); + Vector2 anchor = tileset->autotile_get_size(t_id); + anchor.x += tileset->autotile_get_spacing(t_id); + anchor.y += tileset->autotile_get_spacing(t_id); + anchor.x *= coord.x; + anchor.y *= coord.y; + Ref<OccluderPolygon2D> shape = E->value(); + if (shape.is_valid()) { + Color c_bg; + Color c_border; + if (coord == edited_shape_coord && shape == edited_occlusion_shape) { + c_bg = Color(0, 1, 1, 0.5); + c_border = Color(0, 1, 1); + } else { + c_bg = Color(0.9, 0.7, 0.07, 0.5); + c_border = Color(0.9, 0.7, 0.07, 1); } - workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); - if (shape == edited_occlusion_shape) { + Vector<Vector2> polygon; + Vector<Color> colors; + if (shape == edited_occlusion_shape && current_shape.size() > 2) { for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); + polygon.push_back(current_shape[j]); + colors.push_back(c_bg); + } + } else { + for (int j = 0; j < shape->get_polygon().size(); j++) { + polygon.push_back(shape->get_polygon()[j] + anchor); + colors.push_back(c_bg); + } + } + workspace->draw_polygon(polygon, colors); + if (coord == edited_shape_coord) { + for (int j = 0; j < shape->get_polygon().size() - 1; j++) { + workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true); + } + workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); + if (shape == edited_occlusion_shape) { + draw_handles = true; } } } @@ -1393,49 +1446,77 @@ void AutotileEditor::draw_polygon_shapes() { } } break; case EDITMODE_NAVIGATION: { - Map<Vector2, Ref<NavigationPolygon> > map = tile_set->autotile_get_navigation_map(t_id); - for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { - Vector2 coord = E->key(); - Vector2 anchor = tile_set->autotile_get_size(t_id); - anchor.x += tile_set->autotile_get_spacing(t_id); - anchor.y += tile_set->autotile_get_spacing(t_id); - anchor.x *= coord.x; - anchor.y *= coord.y; - Ref<NavigationPolygon> shape = E->value(); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + Ref<NavigationPolygon> shape = edited_navigation_shape; + if (shape.is_valid()) { - Color c_bg; - Color c_border; - if (coord == edited_shape_coord && shape == edited_navigation_shape) { - c_bg = Color(0, 1, 1, 0.5); - c_border = Color(0, 1, 1); - } else { - c_bg = Color(0.9, 0.7, 0.07, 0.5); - c_border = Color(0.9, 0.7, 0.07, 1); - } + Color c_bg = Color(0, 1, 1, 0.5); + Color c_border = Color(0, 1, 1); + Vector<Vector2> polygon; Vector<Color> colors; - if (shape == edited_navigation_shape && current_shape.size() > 2) { - for (int j = 0; j < current_shape.size(); j++) { - polygon.push_back(current_shape[j]); - colors.push_back(c_bg); - } - } else if (shape->get_polygon_count() > 0) { + + PoolVector<Vector2> vertices = shape->get_vertices(); + for (int j = 0; j < shape->get_polygon(0).size(); j++) { + polygon.push_back(vertices[shape->get_polygon(0)[j]]); + colors.push_back(c_bg); + } + workspace->draw_polygon(polygon, colors); + + if (shape->get_polygon_count() > 0) { PoolVector<Vector2> vertices = shape->get_vertices(); - for (int j = 0; j < shape->get_polygon(0).size(); j++) { - polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor); - colors.push_back(c_bg); + for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) { + workspace->draw_line(vertices[shape->get_polygon(0)[j]], vertices[shape->get_polygon(0)[j + 1]], c_border, 1, true); + } + if (shape == edited_navigation_shape) { + draw_handles = true; } } - workspace->draw_polygon(polygon, colors); - if (coord == edited_shape_coord) { - if (shape->get_polygon_count() > 0) { + } + + } else { + Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id); + for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { + Vector2 coord = E->key(); + Vector2 anchor = tileset->autotile_get_size(t_id); + anchor.x += tileset->autotile_get_spacing(t_id); + anchor.y += tileset->autotile_get_spacing(t_id); + anchor.x *= coord.x; + anchor.y *= coord.y; + Ref<NavigationPolygon> shape = E->value(); + if (shape.is_valid()) { + Color c_bg; + Color c_border; + if (coord == edited_shape_coord && shape == edited_navigation_shape) { + c_bg = Color(0, 1, 1, 0.5); + c_border = Color(0, 1, 1); + } else { + c_bg = Color(0.9, 0.7, 0.07, 0.5); + c_border = Color(0.9, 0.7, 0.07, 1); + } + Vector<Vector2> polygon; + Vector<Color> colors; + if (shape == edited_navigation_shape && current_shape.size() > 2) { + for (int j = 0; j < current_shape.size(); j++) { + polygon.push_back(current_shape[j]); + colors.push_back(c_bg); + } + } else if (shape->get_polygon_count() > 0) { PoolVector<Vector2> vertices = shape->get_vertices(); - for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) { - workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true); + for (int j = 0; j < shape->get_polygon(0).size(); j++) { + polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor); + colors.push_back(c_bg); } - if (shape == edited_navigation_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); + } + workspace->draw_polygon(polygon, colors); + if (coord == edited_shape_coord) { + if (shape->get_polygon_count() > 0) { + PoolVector<Vector2> vertices = shape->get_vertices(); + for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) { + workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true); + } + if (shape == edited_navigation_shape) { + draw_handles = true; } } } @@ -1452,7 +1533,7 @@ void AutotileEditor::draw_polygon_shapes() { } } -void AutotileEditor::close_shape(const Vector2 &shape_anchor) { +void TileSetEditor::close_shape(const Vector2 &shape_anchor) { creating_shape = false; @@ -1477,7 +1558,11 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) { shape->set_points(segments); - tile_set->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + tileset->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord); + else + tileset->tile_set_shape(get_current_tile(), 0, shape); + edited_collision_shape = shape; } @@ -1497,7 +1582,10 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) { w = PoolVector<Vector2>::Write(); shape->set_polygon(polygon); - tile_set->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + tileset->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord); + else + tileset->tile_set_light_occluder(get_current_tile(), shape); edited_occlusion_shape = shape; tools[TOOL_SELECT]->set_pressed(true); workspace->update(); @@ -1517,55 +1605,99 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) { w = PoolVector<Vector2>::Write(); shape->set_vertices(polygon); shape->add_polygon(indices); - tile_set->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord); + + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + tileset->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord); + else + tileset->tile_set_navigation_polygon(get_current_tile(), shape); edited_navigation_shape = shape; tools[TOOL_SELECT]->set_pressed(true); workspace->update(); } + tileset->_change_notify(""); } -void AutotileEditor::select_coord(const Vector2 &coord) { - int spacing = tile_set->autotile_get_spacing(get_current_tile()); - Vector2 size = tile_set->autotile_get_size(get_current_tile()); - Vector2 shape_anchor = coord; - shape_anchor.x *= (size.x + spacing); - shape_anchor.y *= (size.y + spacing); - 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); +void TileSetEditor::select_coord(const Vector2 &coord) { + current_shape = PoolVector2Array(); + 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); + 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())) + edited_navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile()); + + 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]); + } } - } - } else if (edit_mode == EDITMODE_OCCLUSION) { - current_shape.resize(0); - if (edited_occlusion_shape.is_valid()) { - for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) { - current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor); + } else if (edit_mode == EDITMODE_OCCLUSION) { + current_shape.resize(0); + if (edited_occlusion_shape.is_valid()) { + for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) { + current_shape.push_back(edited_occlusion_shape->get_polygon()[i]); + } + } + } else if (edit_mode == EDITMODE_NAVIGATION) { + current_shape.resize(0); + if (edited_navigation_shape.is_valid()) { + if (edited_navigation_shape->get_polygon_count() > 0) { + PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices(); + for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) { + current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]]); + } + } } } - } else if (edit_mode == EDITMODE_NAVIGATION) { - current_shape.resize(0); - if (edited_navigation_shape.is_valid()) { - if (edited_navigation_shape->get_polygon_count() > 0) { - PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices(); - for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) { - current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor); + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + Vector2 shape_anchor = coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); + 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); + } + } + } else if (edit_mode == EDITMODE_OCCLUSION) { + current_shape.resize(0); + if (edited_occlusion_shape.is_valid()) { + for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) { + current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor); + } + } + } else if (edit_mode == EDITMODE_NAVIGATION) { + current_shape.resize(0); + if (edited_navigation_shape.is_valid()) { + if (edited_navigation_shape->get_polygon_count() > 0) { + PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices(); + for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) { + current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor); + } } } } } } -Vector2 AutotileEditor::snap_point(const Vector2 &point) { +Vector2 TileSetEditor::snap_point(const Vector2 &point) { Vector2 p = point; Vector2 coord = edited_shape_coord; - Vector2 tile_size = tile_set->autotile_get_size(get_current_tile()); - int spacing = tile_set->autotile_get_spacing(get_current_tile()); + Vector2 tile_size = tileset->autotile_get_size(get_current_tile()); + int spacing = tileset->autotile_get_spacing(get_current_tile()); Vector2 anchor = coord; anchor.x *= (tile_size.x + spacing); anchor.y *= (tile_size.y + spacing); Rect2 region(anchor, tile_size); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) + region.position = Point2(0, 0); + if (tools[SHAPE_GRID_SNAP]->is_pressed()) { p.x = Math::snap_scalar_seperation(snap_offset.x, snap_step.x, p.x, snap_separation.x); p.y = Math::snap_scalar_seperation(snap_offset.y, snap_step.y, p.y, snap_separation.y); @@ -1583,86 +1715,138 @@ Vector2 AutotileEditor::snap_point(const Vector2 &point) { return p; } -void AutotileEditor::edit(Object *p_node) { +void TileSetEditor::update_tile_list() { + int selected_tile = get_current_tile(); - tile_set = Ref<TileSet>(Object::cast_to<TileSet>(p_node)); - tile_set->add_change_receptor(this); - helper->set_tileset(tile_set); + if (selected_tile < 0) + selected_tile = 0; - autotile_list->clear(); + helper->set_tileset(tileset); + + tile_list->clear(); List<int> ids; - tile_set->get_tile_list(&ids); + tileset->get_tile_list(&ids); for (List<int>::Element *E = ids.front(); E; E = E->next()) { - if (tile_set->tile_get_is_autotile(E->get())) { - autotile_list->add_item(tile_set->tile_get_name(E->get())); - autotile_list->set_item_metadata(autotile_list->get_item_count() - 1, E->get()); - autotile_list->set_item_icon(autotile_list->get_item_count() - 1, tile_set->tile_get_texture(E->get())); - Rect2 region = tile_set->tile_get_region(E->get()); - region.size = tile_set->autotile_get_size(E->get()); - Vector2 pos = tile_set->autotile_get_icon_coordinate(E->get()); - pos.x *= (tile_set->autotile_get_spacing(E->get()) + region.size.x); - pos.y *= (tile_set->autotile_get_spacing(E->get()) + region.size.y); + tile_list->add_item(tileset->tile_get_name(E->get())); + tile_list->set_item_metadata(tile_list->get_item_count() - 1, E->get()); + tile_list->set_item_icon(tile_list->get_item_count() - 1, tileset->tile_get_texture(E->get())); + Rect2 region = tileset->tile_get_region(E->get()); + if (tileset->tile_get_tile_mode(E->get()) == TileSet::AUTO_TILE) { + region.size = tileset->autotile_get_size(E->get()); + Vector2 pos = tileset->autotile_get_icon_coordinate(E->get()); + pos.x *= (tileset->autotile_get_spacing(E->get()) + region.size.x); + pos.y *= (tileset->autotile_get_spacing(E->get()) + region.size.y); region.position += pos; - autotile_list->set_item_icon_region(autotile_list->get_item_count() - 1, region); } + tile_list->set_item_icon_region(tile_list->get_item_count() - 1, region); + tile_list->set_item_icon_modulate(tile_list->get_item_count() - 1, tileset->tile_get_modulate(E->get())); } - if (autotile_list->get_item_count() > 0) { - autotile_list->select(0); - _on_autotile_selected(0); + if (tile_list->get_item_count() > 0 && selected_tile < tile_list->get_item_count()) { + tile_list->select(selected_tile); + _on_tile_list_selected(selected_tile); } helper->_change_notify(""); } -int AutotileEditor::get_current_tile() { +void TileSetEditor::update_tile_list_icon() { + List<int> ids; + tileset->get_tile_list(&ids); + int current_idx = 0; + for (List<int>::Element *E = ids.front(); E; E = E->next()) { + if (current_idx >= tile_list->get_item_count()) + break; + + Rect2 region = tileset->tile_get_region(E->get()); + if (tileset->tile_get_tile_mode(E->get()) == TileSet::AUTO_TILE) { + region.size = tileset->autotile_get_size(E->get()); + Vector2 pos = tileset->autotile_get_icon_coordinate(E->get()); + pos.x *= (tileset->autotile_get_spacing(E->get()) + region.size.x); + pos.y *= (tileset->autotile_get_spacing(E->get()) + region.size.y); + region.position += pos; + } + tile_list->set_item_metadata(current_idx, E->get()); + tile_list->set_item_icon(current_idx, tileset->tile_get_texture(E->get())); + tile_list->set_item_icon_region(current_idx, region); + tile_list->set_item_icon_modulate(current_idx, tileset->tile_get_modulate(E->get())); + tile_list->set_item_text(current_idx, tileset->tile_get_name(E->get())); + current_idx += 1; + } + tile_list->update(); +} - if (autotile_list->get_selected_items().size() == 0) +void TileSetEditor::update_workspace_tile_mode() { + if (get_current_tile() < 0) + return; + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) { + tool_editmode[EDITMODE_COLLISION]->set_pressed(true); + _on_edit_mode_changed(EDITMODE_COLLISION); + } else { + select_coord(Vector2(0, 0)); + } + + tool_editmode[EDITMODE_ICON]->hide(); + tool_editmode[EDITMODE_BITMASK]->hide(); + tool_editmode[EDITMODE_PRIORITY]->hide(); + property_editor->hide(); + } else { + tool_editmode[EDITMODE_ICON]->show(); + tool_editmode[EDITMODE_BITMASK]->show(); + tool_editmode[EDITMODE_PRIORITY]->show(); + property_editor->show(); + } +} + +int TileSetEditor::get_current_tile() { + if (tile_list->get_selected_items().size() == 0) return -1; else - return autotile_list->get_item_metadata(autotile_list->get_selected_items()[0]); + return tile_list->get_item_metadata(tile_list->get_selected_items()[0]); } -void AutotileEditorHelper::set_tileset(const Ref<TileSet> &p_tileset) { +void TileSetEditorHelper::set_tileset(const Ref<TileSet> &p_tileset) { - tile_set = p_tileset; + tileset = p_tileset; } -bool AutotileEditorHelper::_set(const StringName &p_name, const Variant &p_value) { +bool TileSetEditorHelper::_set(const StringName &p_name, const Variant &p_value) { - if (autotile_editor->get_current_tile() < 0 || tile_set.is_null()) + if (selected_tile < 0 || tileset.is_null()) return false; String name = p_name.operator String(); bool v = false; if (name == "bitmask_mode") { - tile_set->set(String::num(autotile_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v); + tileset->set(String::num(selected_tile, 0) + "/autotile/bitmask_mode", p_value, &v); } else if (name.left(7) == "layout/") { - tile_set->set(String::num(autotile_editor->get_current_tile(), 0) + "/autotile" + name.right(6), p_value, &v); + tileset->set(String::num(selected_tile, 0) + "/autotile" + name.right(6), p_value, &v); } if (v) { - tile_set->_change_notify(""); - autotile_editor->workspace->update(); + tileset->_change_notify("autotile"); } return v; } -bool AutotileEditorHelper::_get(const StringName &p_name, Variant &r_ret) const { +bool TileSetEditorHelper::_get(const StringName &p_name, Variant &r_ret) const { - if (autotile_editor->get_current_tile() < 0 || tile_set.is_null()) + if (selected_tile < 0 || tileset.is_null()) + return false; + if (!tileset->has_tile(selected_tile)) return false; String name = p_name.operator String(); bool v = false; if (name == "bitmask_mode") { - r_ret = tile_set->get(String::num(autotile_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", &v); + r_ret = tileset->get(String::num(selected_tile, 0) + "/autotile/bitmask_mode", &v); } else if (name.left(7) == "layout/") { - r_ret = tile_set->get(String::num(autotile_editor->get_current_tile(), 0) + "/autotile" + name.right(6), &v); + r_ret = tileset->get(String::num(selected_tile, 0) + "/autotile" + name.right(6), &v); } return v; } -void AutotileEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const { +void TileSetEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const { - if (autotile_editor->get_current_tile() < 0 || tile_set.is_null()) + if (selected_tile < 0 || tileset.is_null()) return; p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3")); @@ -1670,7 +1854,72 @@ void AutotileEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const p_list->push_back(PropertyInfo(Variant::INT, "layout/spacing", PROPERTY_HINT_RANGE, "0,256,1")); } -AutotileEditorHelper::AutotileEditorHelper(AutotileEditor *p_autotile_editor) { +TileSetEditorHelper::TileSetEditorHelper(TileSetEditor *p_tileset_editor) { + + tileset_editor = p_tileset_editor; + selected_tile = -1; +} + +void TileSetEditorPlugin::edit(Object *p_node) { + + if (Object::cast_to<TileSet>(p_node)) { + tileset_editor->edit(Object::cast_to<TileSet>(p_node)); + tileset_editor->show(); + tileset_editor->texture_region_editor->edit(p_node); + } else + tileset_editor->hide(); +} + +bool TileSetEditorPlugin::handles(Object *p_node) const { + + return p_node->is_class("TileSet"); +} + +void TileSetEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + tileset_editor->show(); + tileset_editor->menu->show(); + tileset_editor_button->show(); + tileset_editor->side_panel->show(); + if (tileset_editor_button->is_pressed()) { + tileset_editor->bottom_panel->show(); + } + texture_region_button->show(); + if (texture_region_button->is_pressed()) + tileset_editor->texture_region_editor->show(); + } else { + tileset_editor->hide(); + tileset_editor->menu->hide(); + tileset_editor->side_panel->hide(); + tileset_editor->bottom_panel->hide(); + tileset_editor_button->hide(); + texture_region_button->hide(); + tileset_editor->texture_region_editor->hide(); + } +} + +TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) { + + tileset_editor = memnew(TileSetEditor(p_node)); + + add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, tileset_editor); + tileset_editor->set_anchors_and_margins_preset(Control::PRESET_TOP_WIDE); + tileset_editor->set_end(Point2(0, 22)); + tileset_editor->hide(); + + tileset_editor->texture_region_editor = memnew(TextureRegionEditor(p_node)); + texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), tileset_editor->texture_region_editor); + texture_region_button->set_tooltip(TTR("Texture Region Editor")); + + tileset_editor->texture_region_editor->set_custom_minimum_size(Size2(0, 200)); + tileset_editor->texture_region_editor->hide(); + texture_region_button->hide(); - autotile_editor = p_autotile_editor; + add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, tileset_editor->side_panel); + tileset_editor->side_panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); + tileset_editor->side_panel->set_custom_minimum_size(Size2(200, 0)); + tileset_editor->side_panel->hide(); + tileset_editor_button = p_node->add_bottom_panel_item(TTR("Tile Set"), tileset_editor->bottom_panel); + tileset_editor_button->hide(); } diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 93d403deea..4894d641a3 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -33,35 +33,38 @@ #include "editor/editor_name_dialog.h" #include "editor/editor_node.h" +#include "editor/plugins/texture_region_editor_plugin.h" #include "scene/2d/sprite.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/tile_set.h" -class AutotileEditorHelper; -class AutotileEditor : public Control { +class TileSetEditorHelper; + +class TileSetEditor : public Control { friend class TileSetEditorPlugin; - friend class AutotileEditorHelper; - GDCLASS(AutotileEditor, Control); + friend class TextureRegionEditor; + + GDCLASS(TileSetEditor, Control); enum EditMode { - EDITMODE_ICON, - EDITMODE_BITMASK, EDITMODE_COLLISION, EDITMODE_OCCLUSION, EDITMODE_NAVIGATION, + EDITMODE_BITMASK, EDITMODE_PRIORITY, + EDITMODE_ICON, EDITMODE_MAX }; - enum AutotileToolbars { + enum TileSetToolbar { TOOLBAR_DUMMY, TOOLBAR_BITMASK, TOOLBAR_SHAPE, TOOLBAR_MAX }; - enum AutotileTools { + enum TileSetTools { TOOL_SELECT, BITMASK_COPY, BITMASK_PASTE, @@ -78,17 +81,18 @@ class AutotileEditor : public Control { TOOL_MAX }; - Ref<TileSet> tile_set; + Ref<TileSet> tileset; + Ref<ConvexPolygonShape2D> edited_collision_shape; Ref<OccluderPolygon2D> edited_occlusion_shape; Ref<NavigationPolygon> edited_navigation_shape; - EditorNode *editor; - int current_item_index; Sprite *preview; ScrollContainer *scroll; Control *workspace_container; + bool draw_handles; + Control *workspace_overlay; Control *workspace; Button *tool_editmode[EDITMODE_MAX]; HBoxContainer *tool_containers[TOOLBAR_MAX]; @@ -114,21 +118,51 @@ class AutotileEditor : public Control { PoolVector2Array current_shape; Map<Vector2, uint16_t> bitmask_map_copy; + EditorNode *editor; + TextureRegionEditor *texture_region_editor; + Control *bottom_panel; Control *side_panel; - ItemList *autotile_list; + ItemList *tile_list; PropertyEditor *property_editor; - AutotileEditorHelper *helper; + TileSetEditorHelper *helper; + + MenuButton *menu; + ConfirmationDialog *cd; + EditorNameDialog *nd; + AcceptDialog *err_dialog; + + enum { - AutotileEditor(EditorNode *p_editor); + MENU_OPTION_ADD_ITEM, + MENU_OPTION_REMOVE_ITEM, + MENU_OPTION_CREATE_FROM_SCENE, + MENU_OPTION_MERGE_FROM_SCENE + }; + + int option; + void _menu_cbk(int p_option); + void _menu_confirm(); + void _name_dialog_confirm(const String &name); + + static void _import_node(Node *p_node, Ref<TileSet> p_library); + static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge); protected: static void _bind_methods(); void _notification(int p_what); virtual void _changed_callback(Object *p_changed, const char *p_prop); +public: + void edit(const Ref<TileSet> &p_tileset); + static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge = true); + + TileSetEditor(EditorNode *p_editor); + ~TileSetEditor(); + private: - void _on_autotile_selected(int p_index); + void _on_tile_list_selected(int p_index); void _on_edit_mode_changed(int p_edit_mode); + void _on_workspace_overlay_draw(); void _on_workspace_draw(); void _on_workspace_input(const Ref<InputEvent> &p_ie); void _on_tool_clicked(int p_tool); @@ -141,24 +175,28 @@ private: void _set_snap_sep_x(float p_val); void _set_snap_sep_y(float p_val); + void initialize_bottom_editor(); void draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>()); void draw_grid_snap(); void draw_polygon_shapes(); void close_shape(const Vector2 &shape_anchor); void select_coord(const Vector2 &coord); Vector2 snap_point(const Vector2 &point); + void update_tile_list(); + void update_tile_list_icon(); + void update_workspace_tile_mode(); - void edit(Object *p_node); int get_current_tile(); }; -class AutotileEditorHelper : public Object { +class TileSetEditorHelper : public Object { - friend class AutotileEditor; - GDCLASS(AutotileEditorHelper, Object); + friend class TileSetEditor; + GDCLASS(TileSetEditorHelper, Object); - Ref<TileSet> tile_set; - AutotileEditor *autotile_editor; + Ref<TileSet> tileset; + TileSetEditor *tileset_editor; + int selected_tile; public: void set_tileset(const Ref<TileSet> &p_tileset); @@ -168,46 +206,7 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - AutotileEditorHelper(AutotileEditor *p_autotile_editor); -}; - -class TileSetEditor : public Control { - - friend class TileSetEditorPlugin; - GDCLASS(TileSetEditor, Control); - - Ref<TileSet> tileset; - - EditorNode *editor; - MenuButton *menu; - ConfirmationDialog *cd; - EditorNameDialog *nd; - AcceptDialog *err_dialog; - - enum { - - MENU_OPTION_ADD_ITEM, - MENU_OPTION_REMOVE_ITEM, - MENU_OPTION_CREATE_FROM_SCENE, - MENU_OPTION_MERGE_FROM_SCENE - }; - - int option; - void _menu_cbk(int p_option); - void _menu_confirm(); - void _name_dialog_confirm(const String &name); - - static void _import_node(Node *p_node, Ref<TileSet> p_library); - static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge); - -protected: - static void _bind_methods(); - -public: - void edit(const Ref<TileSet> &p_tileset); - static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge = true); - - TileSetEditor(EditorNode *p_editor); + TileSetEditorHelper(TileSetEditor *p_tileset_editor); }; class TileSetEditorPlugin : public EditorPlugin { @@ -215,10 +214,10 @@ class TileSetEditorPlugin : public EditorPlugin { GDCLASS(TileSetEditorPlugin, EditorPlugin); TileSetEditor *tileset_editor; - AutotileEditor *autotile_editor; EditorNode *editor; - ToolButton *autotile_button; + ToolButton *tileset_editor_button; + ToolButton *texture_region_button; public: virtual String get_name() const { return "TileSet"; } |