diff options
Diffstat (limited to 'editor/plugins')
34 files changed, 1570 insertions, 438 deletions
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index bfee76492b..dff6eb5c5e 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -471,7 +471,7 @@ void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) { Ref<AnimationNode> an = blend_tree->get_node(p_which); - ERR_FAIL_COND(!an.is_valid()) + ERR_FAIL_COND(!an.is_valid()); AnimationTreeEditor::get_singleton()->enter_editor(p_which); } @@ -798,7 +798,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima String new_name = p_text; - ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) + ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1); if (new_name == prev_name) { return; //nothing to do diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 7c075b5635..5204565c06 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -54,13 +54,9 @@ void AnimationPlayerEditor::_node_removed(Node *p_node) { track_editor->set_root(NULL); track_editor->show_select_node_warning(true); _update_player(); - //editor->animation_editor_make_visible(false); } } -void AnimationPlayerEditor::_gui_input(Ref<InputEvent> p_event) { -} - void AnimationPlayerEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PROCESS: { @@ -129,8 +125,10 @@ void AnimationPlayerEditor::_notification(int p_what) { autoplay_icon = get_icon("AutoPlay", "EditorIcons"); stop->set_icon(get_icon("Stop", "EditorIcons")); + onion_toggle->set_icon(get_icon("Onion", "EditorIcons")); + onion_skinning->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + pin->set_icon(get_icon("Pin", "EditorIcons")); - onion_skinning->set_icon(get_icon("Onion", "EditorIcons")); tool_anim->add_style_override("normal", get_stylebox("normal", "Button")); track_editor->get_edit_menu()->add_style_override("normal", get_stylebox("normal", "Button")); @@ -677,19 +675,22 @@ Dictionary AnimationPlayerEditor::get_state() const { } void AnimationPlayerEditor::set_state(const Dictionary &p_state) { - if (p_state.has("visible") && p_state["visible"]) { + if (!p_state.has("visible") || !p_state["visible"]) { + return; + } + if (!EditorNode::get_singleton()->get_edited_scene()) { + return; + } - if (!EditorNode::get_singleton()->get_edited_scene()) - return; + if (p_state.has("player")) { Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]); if (Object::cast_to<AnimationPlayer>(n) && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) { player = Object::cast_to<AnimationPlayer>(n); _update_player(); - show(); + editor->make_bottom_panel_item_visible(this); set_process(true); ensure_visibility(); - //EditorNode::get_singleton()->animation_panel_make_visible(true); if (p_state.has("animation")) { String anim = p_state["animation"]; @@ -697,10 +698,10 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { _animation_edit(); } } + } - if (p_state.has("track_editor_state")) { - track_editor->set_state(p_state["track_editor_state"]); - } + if (p_state.has("track_editor_state")) { + track_editor->set_state(p_state["track_editor_state"]); } } @@ -719,17 +720,17 @@ void AnimationPlayerEditor::_animation_edit() { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); track_editor->set_animation(anim); + Node *root = player->get_node(player->get_root()); if (root) { track_editor->set_root(root); } - } else { - track_editor->set_animation(Ref<Animation>()); track_editor->set_root(NULL); } } + void AnimationPlayerEditor::_dialog_action(String p_file) { switch (current_option) { @@ -768,7 +769,7 @@ void AnimationPlayerEditor::_dialog_action(String p_file) { if (current != "") { Ref<Animation> anim = player->get_animation(current); - ERR_FAIL_COND(!Object::cast_to<Resource>(*anim)) + ERR_FAIL_COND(!Object::cast_to<Resource>(*anim)); RES current_res = RES(Object::cast_to<Resource>(*anim)); @@ -879,8 +880,6 @@ void AnimationPlayerEditor::_update_player() { _animation_selected(0); } - //pause->set_pressed(player->is_paused()); - if (animation->get_item_count()) { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); @@ -908,8 +907,6 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { track_editor->show_select_node_warning(false); } else { track_editor->show_select_node_warning(true); - - //hide(); } } @@ -1108,7 +1105,6 @@ void AnimationPlayerEditor::_hide_anim_editors() { track_editor->set_animation(Ref<Animation>()); track_editor->set_root(NULL); track_editor->show_select_node_warning(true); - //editor->animation_editor_make_visible(false); } void AnimationPlayerEditor::_animation_about_to_show_menu() { @@ -1229,7 +1225,6 @@ void AnimationPlayerEditor::_onion_skinning_menu(int p_option) { case ONION_SKINNING_ENABLE: { onion.enabled = !onion.enabled; - menu->set_item_checked(idx, onion.enabled); if (onion.enabled) _start_onion_skinning(); @@ -1430,6 +1425,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { new_state["show_rulers"] = false; new_state["show_guides"] = false; new_state["show_helpers"] = false; + new_state["show_zoom_control"] = false; // TODO: Save/restore only affected entries CanvasItemEditor::get_singleton()->set_state(new_state); } @@ -1482,7 +1478,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { if (valid) { player->seek(pos, true); get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials - values_backup.update_skeletons(); // Needed for Skeletons + values_backup.update_skeletons(); // Needed for Skeletons (2D & 3D) VS::get_singleton()->viewport_set_active(onion.captures[cidx], true); VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]); @@ -1546,7 +1542,6 @@ void AnimationPlayerEditor::_pin_pressed() { void AnimationPlayerEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &AnimationPlayerEditor::_gui_input); ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationPlayerEditor::_node_removed); ClassDB::bind_method(D_METHOD("_play_pressed"), &AnimationPlayerEditor::_play_pressed); ClassDB::bind_method(D_METHOD("_play_from_pressed"), &AnimationPlayerEditor::_play_from_pressed); @@ -1609,12 +1604,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay player = NULL; - Label *l; - - /*l= memnew( Label ); - l->set_text("Animation Player:"); - add_child(l);*/ - HBoxContainer *hb = memnew(HBoxContainer); add_child(hb); @@ -1639,10 +1628,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay play_from->set_tooltip(TTR("Play selected animation from current pos. (D)")); hb->add_child(play_from); - //pause = memnew( Button ); - //pause->set_toggle_mode(true); - //hb->add_child(pause); - frame = memnew(SpinBox); hb->add_child(frame); frame->set_custom_minimum_size(Size2(60, 0)); @@ -1668,7 +1653,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay tool_anim = memnew(MenuButton); tool_anim->set_flat(false); - //tool_anim->set_flat(false); tool_anim->set_tooltip(TTR("Animation Tools")); tool_anim->set_text(TTR("Animation")); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM); @@ -1699,28 +1683,27 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay hb->add_child(autoplay); autoplay->set_tooltip(TTR("Autoplay on Load")); - //tool_anim->get_popup()->add_separator(); - //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM); - hb->add_child(memnew(VSeparator)); track_editor = memnew(AnimationTrackEditor); hb->add_child(track_editor->get_edit_menu()); + hb->add_child(memnew(VSeparator)); + + onion_toggle = memnew(ToolButton); + onion_toggle->set_toggle_mode(true); + onion_toggle->set_tooltip(TTR("Enable Onion Skinning")); + onion_toggle->connect("pressed", this, "_onion_skinning_menu", varray(ONION_SKINNING_ENABLE)); + hb->add_child(onion_toggle); + onion_skinning = memnew(MenuButton); - //onion_skinning->set_flat(false); - onion_skinning->set_tooltip(TTR("Onion Skinning")); - onion_skinning->get_popup()->add_check_shortcut(ED_SHORTCUT("animation_player_editor/onion_skinning", TTR("Enable Onion Skinning")), ONION_SKINNING_ENABLE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Directions"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->set_tooltip(TTR("Onion Skinning Options")); + onion_skinning->get_popup()->add_separator(TTR("Directions")); onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Depth"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_separator(TTR("Depth")); onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); @@ -1731,6 +1714,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS); hb->add_child(onion_skinning); + hb->add_child(memnew(VSeparator)); + pin = memnew(ToolButton); pin->set_toggle_mode(true); pin->set_tooltip(TTR("Pin AnimationPlayer")); @@ -1747,10 +1732,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay VBoxContainer *vb = memnew(VBoxContainer); name_dialog->add_child(vb); - l = memnew(Label); - l->set_text(TTR("Animation Name:")); - vb->add_child(l); - name_title = l; + name_title = memnew(Label(TTR("Animation Name:"))); + vb->add_child(name_title); name = memnew(LineEdit); vb->add_child(name); @@ -1769,7 +1752,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay blend_editor.dialog->set_hide_on_ok(true); VBoxContainer *blend_vb = memnew(VBoxContainer); blend_editor.dialog->add_child(blend_vb); - //blend_editor.dialog->set_child_rect(blend_vb); blend_editor.tree = memnew(Tree); blend_editor.tree->set_columns(2); blend_vb->add_margin_child(TTR("Blend Times:"), blend_editor.tree, true); @@ -1787,8 +1769,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay play_bw->connect("pressed", this, "_play_bw_pressed"); play_bw_from->connect("pressed", this, "_play_bw_from_pressed"); stop->connect("pressed", this, "_stop_pressed"); - //pause->connect("pressed", this,"_pause_pressed"); - //frame->connect("text_entered", this,"_seek_frame_changed"); animation->connect("item_selected", this, "_animation_selected", Vector<Variant>(), true); @@ -1892,11 +1872,6 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { editor->make_bottom_panel_item_visible(anim_editor); anim_editor->set_process(true); anim_editor->ensure_visibility(); - //editor->animation_panel_make_visible(true); - } else { - - //anim_editor->hide(); - //anim_editor->set_idle_process(false); } } @@ -1905,16 +1880,7 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) { editor = p_node; anim_editor = memnew(AnimationPlayerEditor(editor, this)); anim_editor->set_undo_redo(editor->get_undo_redo()); - editor->add_bottom_panel_item(TTR("Animation"), anim_editor); - /* - editor->get_viewport()->add_child(anim_editor); - anim_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); - anim_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); - anim_editor->set_margin( MARGIN_TOP, 75 ); - anim_editor->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END); - anim_editor->set_margin( MARGIN_RIGHT, 0 );*/ - anim_editor->hide(); } AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() { diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 9085c70410..dedce16bbc 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -97,11 +97,10 @@ class AnimationPlayerEditor : public VBoxContainer { Button *play_from; Button *play_bw; Button *play_bw_from; - - //Button *pause; Button *autoplay; MenuButton *tool_anim; + ToolButton *onion_toggle; MenuButton *onion_skinning; ToolButton *pin; SpinBox *frame; @@ -228,7 +227,6 @@ class AnimationPlayerEditor : public VBoxContainer { protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); void _node_removed(Node *p_node); static void _bind_methods(); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index f06b4b2828..e25e7ac7ed 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1096,7 +1096,7 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) { String new_name = p_text; - ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) + ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1); if (new_name == prev_name) { return; // Nothing to do. diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 0dfb53b34a..1503258ff5 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -35,7 +35,7 @@ #include "editor_node.h" #include "editor_settings.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) { +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, const String &p_cost) { title->set_text(p_title); asset_id = p_asset_id; @@ -44,13 +44,6 @@ void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, co author->set_text(p_author); author_id = p_author_id; price->set_text(p_cost); - - for (int i = 0; i < 5; i++) { - if (i < p_rating) - stars[i]->set_texture(get_icon("Favorites", "EditorIcons")); - else - stars[i]->set_texture(get_icon("NonFavorite", "EditorIcons")); - } } void EditorAssetLibraryItem::set_image(int p_type, int p_index, const Ref<Texture> &p_image) { @@ -65,9 +58,10 @@ void EditorAssetLibraryItem::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - icon->set_normal_texture(get_icon("DefaultProjectIcon", "EditorIcons")); + icon->set_normal_texture(get_icon("ProjectIconLoading", "EditorIcons")); category->add_color_override("font_color", Color(0.5, 0.5, 0.5)); author->add_color_override("font_color", Color(0.5, 0.5, 0.5)); + price->add_color_override("font_color", Color(0.5, 0.5, 0.5)); } } @@ -100,17 +94,19 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() { Ref<StyleBoxEmpty> border; border.instance(); - border->set_default_margin(MARGIN_LEFT, 5); - border->set_default_margin(MARGIN_RIGHT, 5); - border->set_default_margin(MARGIN_BOTTOM, 5); - border->set_default_margin(MARGIN_TOP, 5); + border->set_default_margin(MARGIN_LEFT, 5 * EDSCALE); + border->set_default_margin(MARGIN_RIGHT, 5 * EDSCALE); + border->set_default_margin(MARGIN_BOTTOM, 5 * EDSCALE); + border->set_default_margin(MARGIN_TOP, 5 * EDSCALE); add_style_override("panel", border); HBoxContainer *hb = memnew(HBoxContainer); + // Add some spacing to visually separate the icon from the asset details + hb->add_constant_override("separation", 15 * EDSCALE); add_child(hb); icon = memnew(TextureButton); - icon->set_custom_minimum_size(Size2(64, 64)); + icon->set_custom_minimum_size(Size2(64, 64) * EDSCALE); icon->set_default_cursor_shape(CURSOR_POINTING_HAND); icon->connect("pressed", this, "_asset_clicked"); @@ -139,18 +135,11 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() { author->connect("pressed", this, "_author_clicked"); vb->add_child(author); - HBoxContainer *rating_hb = memnew(HBoxContainer); - vb->add_child(rating_hb); - - for (int i = 0; i < 5; i++) { - stars[i] = memnew(TextureRect); - rating_hb->add_child(stars[i]); - } price = memnew(Label); price->set_text(TTR("Free")); vb->add_child(price); - set_custom_minimum_size(Size2(250, 100)); + set_custom_minimum_size(Size2(250, 100) * EDSCALE); set_h_size_flags(SIZE_EXPAND_FILL); set_mouse_filter(MOUSE_FILTER_PASS); @@ -194,7 +183,6 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const break; } } - //item->call("set_image",p_type,p_index,p_image); } break; case EditorAssetLibrary::IMAGE_QUEUE_SCREENSHOT: { @@ -207,7 +195,6 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const break; } } - //item->call("set_image",p_type,p_index,p_image); } break; } } @@ -248,13 +235,13 @@ void EditorAssetLibraryItemDescription::_preview_click(int p_id) { } } -void EditorAssetLibraryItemDescription::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, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash) { +void EditorAssetLibraryItemDescription::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, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash) { asset_id = p_asset_id; title = p_title; download_url = p_download_url; sha256 = p_sha256_hash; - item->configure(p_title, p_asset_id, p_category, p_category_id, p_author, p_author_id, p_rating, p_cost); + item->configure(p_title, p_asset_id, p_category, p_category_id, p_author, p_author_id, p_cost); description->clear(); description->add_text(TTR("Version:") + " " + p_version_string + "\n"); description->add_text(TTR("Contents:") + " "); @@ -554,7 +541,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { hb2->add_child(retry); hb2->add_child(install); - set_custom_minimum_size(Size2(310, 0)); + set_custom_minimum_size(Size2(310, 0) * EDSCALE); download = memnew(HTTPRequest); add_child(download); @@ -666,7 +653,6 @@ void EditorAssetLibrary::_install_asset() { } const char *EditorAssetLibrary::sort_key[SORT_MAX] = { - "rating", "downloads", "name", "cost", @@ -674,10 +660,9 @@ const char *EditorAssetLibrary::sort_key[SORT_MAX] = { }; const char *EditorAssetLibrary::sort_text[SORT_MAX] = { - "Rating", "Downloads", "Name", - "Cost", + "License", // "cost" stores the SPDX license name in the Godot Asset Library "Updated" }; @@ -733,6 +718,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt image_data = cached_data; file->close(); + memdelete(file); } } @@ -807,6 +793,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons if (file) { file->store_line(new_etag); file->close(); + memdelete(file); } int len = p_data.size(); @@ -816,6 +803,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons file->store_32(len); file->store_buffer(r.ptr(), len); file->close(); + memdelete(file); } break; @@ -855,6 +843,7 @@ void EditorAssetLibrary::_update_image_queue() { if (file) { headers.push_back("If-None-Match: " + file->get_line()); file->close(); + memdelete(file); } } @@ -984,7 +973,7 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int to = p_page_count; hbc->add_spacer(); - hbc->add_constant_override("separation", 5); + hbc->add_constant_override("separation", 5 * EDSCALE); Button *first = memnew(Button); first->set_text(TTR("First")); @@ -1190,8 +1179,8 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const asset_items = memnew(GridContainer); asset_items->set_columns(2); - asset_items->add_constant_override("hseparation", 10); - asset_items->add_constant_override("vseparation", 10); + asset_items->add_constant_override("hseparation", 10 * EDSCALE); + asset_items->add_constant_override("vseparation", 10 * EDSCALE); library_vb->add_child(asset_items); @@ -1208,12 +1197,11 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const ERR_CONTINUE(!r.has("author_id")); ERR_CONTINUE(!r.has("category_id")); ERR_FAIL_COND(!category_map.has(r["category_id"])); - ERR_CONTINUE(!r.has("rating")); ERR_CONTINUE(!r.has("cost")); EditorAssetLibraryItem *item = memnew(EditorAssetLibraryItem); asset_items->add_child(item); - item->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["rating"], r["cost"]); + item->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"]); item->connect("asset_selected", this, "_select_asset"); item->connect("author_selected", this, "_select_author"); item->connect("category_selected", this, "_select_category"); @@ -1234,7 +1222,6 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const ERR_FAIL_COND(!r.has("version_string")); ERR_FAIL_COND(!r.has("category_id")); ERR_FAIL_COND(!category_map.has(r["category_id"])); - ERR_FAIL_COND(!r.has("rating")); ERR_FAIL_COND(!r.has("cost")); ERR_FAIL_COND(!r.has("description")); ERR_FAIL_COND(!r.has("download_url")); @@ -1250,7 +1237,7 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const description->popup_centered_minsize(); description->connect("confirmed", this, "_install_asset"); - description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["rating"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]); + description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]); /*item->connect("asset_selected",this,"_select_asset"); item->connect("author_selected",this,"_select_author"); item->connect("category_selected",this,"_category_selected");*/ @@ -1357,7 +1344,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { HBoxContainer *search_hb = memnew(HBoxContainer); library_main->add_child(search_hb); - library_main->add_constant_override("separation", 10); + library_main->add_constant_override("separation", 10 * EDSCALE); search_hb->add_child(memnew(Label(TTR("Search:") + " "))); filter = memnew(LineEdit); @@ -1459,10 +1446,10 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { Ref<StyleBoxEmpty> border2; border2.instance(); - border2->set_default_margin(MARGIN_LEFT, 15); - border2->set_default_margin(MARGIN_RIGHT, 35); - border2->set_default_margin(MARGIN_BOTTOM, 15); - border2->set_default_margin(MARGIN_TOP, 15); + border2->set_default_margin(MARGIN_LEFT, 15 * EDSCALE); + border2->set_default_margin(MARGIN_RIGHT, 35 * EDSCALE); + border2->set_default_margin(MARGIN_BOTTOM, 15 * EDSCALE); + border2->set_default_margin(MARGIN_TOP, 15 * EDSCALE); PanelContainer *library_vb_border = memnew(PanelContainer); library_scroll->add_child(library_vb_border); @@ -1474,15 +1461,14 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { library_vb->set_h_size_flags(SIZE_EXPAND_FILL); library_vb_border->add_child(library_vb); - //margin_panel->set_stop_mouse(false); asset_top_page = memnew(HBoxContainer); library_vb->add_child(asset_top_page); asset_items = memnew(GridContainer); asset_items->set_columns(2); - asset_items->add_constant_override("hseparation", 10); - asset_items->add_constant_override("vseparation", 10); + asset_items->add_constant_override("hseparation", 10 * EDSCALE); + asset_items->add_constant_override("vseparation", 10 * EDSCALE); library_vb->add_child(asset_items); @@ -1496,7 +1482,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { last_queue_id = 0; - library_vb->add_constant_override("separation", 20); + library_vb->add_constant_override("separation", 20 * EDSCALE); load_status = memnew(ProgressBar); load_status->set_min(0); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index dd5f3c2077..81288ae831 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -77,7 +77,7 @@ protected: static void _bind_methods(); public: - void 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); + void 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, const String &p_cost); EditorAssetLibraryItem(); }; @@ -120,7 +120,7 @@ protected: static void _bind_methods(); public: - void 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, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash); + void 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, const String &p_cost, int p_version, const String &p_version_string, const String &p_description, const String &p_download_url, const String &p_browse_url, const String &p_sha256_hash); void add_preview(int p_id, bool p_video, const String &p_url); String get_title() { return title; } @@ -216,7 +216,6 @@ class EditorAssetLibrary : public PanelContainer { }; enum SortOrder { - SORT_RATING, SORT_DOWNLOADS, SORT_NAME, SORT_COST, diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index 172096b1a7..0ab3d26c85 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -53,7 +53,6 @@ void AudioStreamEditor::_notification(int p_what) { if (p_what == NOTIFICATION_PROCESS) { _current = _player->get_playback_position(); _indicator->update(); - _preview->update(); } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { @@ -121,15 +120,19 @@ void AudioStreamEditor::_play() { void AudioStreamEditor::_stop() { _player->stop(); - _on_finished(); + _play_button->set_icon(get_icon("MainPlay", "EditorIcons")); + _current = 0; + _indicator->update(); + set_process(false); } void AudioStreamEditor::_on_finished() { _play_button->set_icon(get_icon("MainPlay", "EditorIcons")); - _current = 0; - _indicator->update(); - set_process(false); + if (_current == _player->get_stream()->get_length()) { + _current = 0; + _indicator->update(); + } } void AudioStreamEditor::_draw_indicator() { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index c0f2410636..3c24fd19b6 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -731,6 +731,8 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 ERR_FAIL_COND_V(!p_control, Vector2()); Rect2 parent_rect = p_control->get_parent_anchorable_rect(); + ERR_FAIL_COND_V(parent_rect.size.x == 0, Vector2()); + ERR_FAIL_COND_V(parent_rect.size.y == 0, Vector2()); return (p_control->get_transform().xform(position) - parent_rect.position) / parent_rect.size; } @@ -1081,7 +1083,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { 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)))) { + (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_panning") && pan_pressed))) { // Pan the viewport panning = true; } @@ -1097,7 +1099,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; if (k.is_valid()) { - if (k->get_scancode() == KEY_SPACE && (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || drag_type != DRAG_NONE)) { + bool is_pan_key = pan_view_shortcut.is_valid() && pan_view_shortcut->is_shortcut(p_event); + + if (is_pan_key && (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || drag_type != DRAG_NONE)) { if (!panning) { if (k->is_pressed() && !k->is_echo()) { //Pan the viewport @@ -1110,6 +1114,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { } } } + + if (is_pan_key) + pan_pressed = k->is_pressed(); } Ref<InputEventMouseMotion> m = p_event; @@ -1387,7 +1394,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { // Starts anchor dragging if needed if (drag_type == DRAG_NONE) { - if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT && show_helpers) { + if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1) { Control *control = Object::cast_to<Control>(selection[0]); @@ -2213,7 +2220,8 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { bool accepted = false; - if (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || !Input::get_singleton()->is_key_pressed(KEY_SPACE)) { + + if (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || !pan_pressed) { 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))) { @@ -2512,20 +2520,50 @@ void CanvasItemEditor::_draw_grid() { } } -void CanvasItemEditor::_draw_control_helpers(Control *control) { +void CanvasItemEditor::_draw_control_anchors(Control *control) { Transform2D xform = transform * control->get_global_transform_with_canvas(); RID ci = viewport->get_canvas_item(); + if (tool == TOOL_SELECT && !Object::cast_to<Container>(control->get_parent())) { + + // Compute the anchors + float anchors_values[4]; + anchors_values[0] = control->get_anchor(MARGIN_LEFT); + anchors_values[1] = control->get_anchor(MARGIN_TOP); + anchors_values[2] = control->get_anchor(MARGIN_RIGHT); + anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); + + Vector2 anchors_pos[4]; + for (int i = 0; i < 4; i++) { + Vector2 value = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]); + anchors_pos[i] = xform.xform(_anchor_to_position(control, value)); + } + + // Draw the anchors handles + Rect2 anchor_rects[4]; + anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); + anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); + anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); + anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); + + for (int i = 0; i < 4; i++) { + anchor_handle->draw_rect(ci, anchor_rects[i]); + } + } +} + +void CanvasItemEditor::_draw_control_helpers(Control *control) { + Transform2D xform = transform * control->get_global_transform_with_canvas(); if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) { // Draw the helpers Color color_base = Color(0.8, 0.8, 0.8, 0.5); + // Compute the anchors float anchors_values[4]; anchors_values[0] = control->get_anchor(MARGIN_LEFT); anchors_values[1] = control->get_anchor(MARGIN_TOP); anchors_values[2] = control->get_anchor(MARGIN_RIGHT); anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); - // Draw the anchors Vector2 anchors[4]; Vector2 anchors_pos[4]; for (int i = 0; i < 4; i++) { @@ -2592,16 +2630,6 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { _draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4)); } - Rect2 anchor_rects[4]; - anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); - anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); - anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); - anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); - - for (int i = 0; i < 4; i++) { - anchor_handle->draw_rect(ci, anchor_rects[i]); - } - // Draw the margin values and the node width/height when dragging control side float ratio = 0.33; Transform2D parent_transform = xform * control->get_transform().affine_inverse(); @@ -2779,6 +2807,7 @@ void CanvasItemEditor::_draw_selection() { // Draw control-related helpers Control *control = Object::cast_to<Control>(canvas_item); if (control && _is_node_movable(control)) { + _draw_control_anchors(control); _draw_control_helpers(control); } @@ -3258,6 +3287,7 @@ 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")); + bool has_container_parents = false; int nb_control = 0; int nb_having_pivot = 0; @@ -3301,6 +3331,10 @@ void CanvasItemEditor::_notification(int p_what) { viewport->update(); } nb_control++; + + if (Object::cast_to<Container>(control->get_parent())) { + has_container_parents = true; + } } if (canvas_item->_edit_use_pivot()) { @@ -3311,24 +3345,26 @@ void CanvasItemEditor::_notification(int p_what) { // Activate / Deactivate the pivot tool pivot_button->set_disabled(nb_having_pivot == 0); - // Show / Hide the layout button + // Show / Hide the layout and anchors mode buttons if (nb_control > 0 && nb_control == selection.size()) { presets_menu->set_visible(true); - presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node.")); + anchor_mode_button->set_visible(true); // Disable if the selected node is child of a container - presets_menu->set_disabled(false); - for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { - Control *control = Object::cast_to<Control>(E->get()); - if (!control || Object::cast_to<Container>(control->get_parent())) { - presets_menu->set_disabled(true); - presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); - break; - } + if (has_container_parents) { + presets_menu->set_disabled(true); + presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); + anchor_mode_button->set_disabled(true); + anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); + } else { + presets_menu->set_disabled(false); + presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node.")); + anchor_mode_button->set_disabled(false); + anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins.")); } - } else { presets_menu->set_visible(false); + anchor_mode_button->set_visible(false); } // Update the viewport if bones changes @@ -3436,9 +3472,10 @@ void CanvasItemEditor::_notification(int p_what) { p->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE); p->add_separator(); p->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_AND_MARGINS_PRESET_WIDE); + p->add_icon_item(get_icon("Anchor", "EditorIcons"), "Keep Ratio", ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO); p->add_separator(); - p->add_submenu_item(TTR("Anchors Only"), "Anchors"); - p->set_item_icon(20, get_icon("Anchor", "EditorIcons")); + p->add_submenu_item(TTR("Anchors only"), "Anchors"); + p->set_item_icon(21, get_icon("Anchor", "EditorIcons")); anchors_popup->clear(); anchors_popup->add_icon_item(get_icon("ControlAlignTopLeft", "EditorIcons"), "Top Left", ANCHORS_PRESET_TOP_LEFT); @@ -3460,9 +3497,32 @@ void CanvasItemEditor::_notification(int p_what) { anchors_popup->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_PRESET_HCENTER_WIDE); anchors_popup->add_separator(); anchors_popup->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_PRESET_WIDE); + + anchor_mode_button->set_icon(get_icon("Anchor", "EditorIcons")); } } +void CanvasItemEditor::_selection_changed() { + // Update the anchors_mode + int nbValidControls = 0; + int nbAnchorsMode = 0; + List<Node *> selection = editor_selection->get_selected_node_list(); + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control) + continue; + if (Object::cast_to<Container>(control->get_parent())) + continue; + + nbValidControls++; + if (control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_")) { + nbAnchorsMode++; + } + } + anchors_mode = (nbValidControls == nbAnchorsMode); + anchor_mode_button->set_pressed(anchors_mode); +} + void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { Array selection = editor_selection->get_selected_nodes(); @@ -3680,6 +3740,38 @@ void CanvasItemEditor::_set_anchors_and_margins_preset(Control::LayoutPreset p_p } undo_redo->commit_action(); + + anchors_mode = false; + anchor_mode_button->set_pressed(anchors_mode); +} + +void CanvasItemEditor::_set_anchors_and_margins_to_keep_ratio() { + List<Node *> selection = editor_selection->get_selected_node_list(); + + undo_redo->create_action(TTR("Change Anchors and Margins")); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Control *control = Object::cast_to<Control>(E->get()); + if (control) { + Point2 top_left_anchor = _position_to_anchor(control, Point2()); + Point2 bottom_right_anchor = _position_to_anchor(control, control->get_size()); + undo_redo->add_do_method(control, "set_anchor", MARGIN_LEFT, top_left_anchor.x, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_RIGHT, bottom_right_anchor.x, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_TOP, top_left_anchor.y, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_BOTTOM, bottom_right_anchor.y, false, true); + undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true); + + bool use_anchors = control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_"); + undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); + undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", use_anchors); + + anchors_mode = true; + anchor_mode_button->set_pressed(anchors_mode); + } + } + + undo_redo->commit_action(); } void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { @@ -3811,6 +3903,20 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, } } +void CanvasItemEditor::_button_toggle_anchor_mode(bool p_status) { + List<CanvasItem *> selection = _get_edited_canvas_items(false, false); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control || Object::cast_to<Container>(control->get_parent())) + continue; + + control->set_meta("_edit_use_anchors_", p_status); + } + + anchors_mode = p_status; + viewport->update(); +} + void CanvasItemEditor::_popup_callback(int p_op) { last_option = MenuOption(p_op); @@ -3911,6 +4017,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { show_rulers = !show_rulers; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); + _update_scrollbars(); viewport->update(); } break; case SHOW_GUIDES: { @@ -4047,6 +4154,9 @@ void CanvasItemEditor::_popup_callback(int p_op) { case ANCHORS_AND_MARGINS_PRESET_WIDE: { _set_anchors_and_margins_preset(Control::PRESET_WIDE); } break; + case ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO: { + _set_anchors_and_margins_to_keep_ratio(); + } break; case ANCHORS_PRESET_TOP_LEFT: { _set_anchors_preset(PRESET_TOP_LEFT); @@ -4220,6 +4330,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Create Custom Bone(s) from Node(s)")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to<Node2D>(E->key()); @@ -4229,19 +4340,24 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->get_parent_item()) continue; + if (n2d->has_meta("_edit_bone_") && (bool)n2d->get_meta("_edit_bone_") == true) + continue; - n2d->set_meta("_edit_bone_", true); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "set_meta", "_edit_bone_", true); + undo_redo->add_undo_method(n2d, "remove_meta", "_edit_bone_"); } - _queue_update_bone_list(); - viewport->update(); + undo_redo->add_do_method(this, "_queue_update_bone_list"); + undo_redo->add_undo_method(this, "_queue_update_bone_list"); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_CLEAR_BONES: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Clear Bones")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to<Node2D>(E->key()); @@ -4249,40 +4365,47 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->is_visible_in_tree()) continue; + if (!n2d->has_meta("_edit_bone_")) + continue; - n2d->set_meta("_edit_bone_", Variant()); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "remove_meta", "_edit_bone_"); + undo_redo->add_undo_method(n2d, "set_meta", "_edit_bone_", n2d->get_meta("_edit_bone_")); } - _queue_update_bone_list(); - viewport->update(); + undo_redo->add_do_method(this, "_queue_update_bone_list"); + undo_redo->add_undo_method(this, "_queue_update_bone_list"); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_SET_IK_CHAIN: { List<Node *> selection = editor_selection->get_selected_node_list(); + undo_redo->create_action(TTR("Make IK Chain")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); if (!canvas_item || !canvas_item->is_visible_in_tree()) continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; + if (canvas_item->has_meta("_edit_ik_") && (bool)canvas_item->get_meta("_edit_ik_") == true) + continue; - canvas_item->set_meta("_edit_ik_", true); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_ik_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_ik_"); } - - viewport->update(); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_CLEAR_IK_CHAIN: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Clear IK Chain")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *n2d = Object::cast_to<CanvasItem>(E->key()); @@ -4290,12 +4413,15 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->is_visible_in_tree()) continue; + if (!n2d->has_meta("_edit_ik_")) + continue; - n2d->set_meta("_edit_ik_", Variant()); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "remove_meta", "_edit_ik_"); + undo_redo->add_undo_method(n2d, "set_meta", "_edit_ik_", n2d->get_meta("_edit_ik_")); } - viewport->update(); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; } @@ -4366,6 +4492,7 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_button_zoom_reset", &CanvasItemEditor::_button_zoom_reset); ClassDB::bind_method("_button_zoom_plus", &CanvasItemEditor::_button_zoom_plus); ClassDB::bind_method("_button_toggle_snap", &CanvasItemEditor::_button_toggle_snap); + ClassDB::bind_method("_button_toggle_anchor_mode", &CanvasItemEditor::_button_toggle_anchor_mode); ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll); ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars); ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback); @@ -4376,8 +4503,10 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport); ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport); ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); + ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed); + ClassDB::bind_method("_selection_changed", &CanvasItemEditor::_selection_changed); ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop); ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); @@ -4411,6 +4540,7 @@ Dictionary CanvasItemEditor::get_state() const { state["show_rulers"] = show_rulers; state["show_guides"] = show_guides; state["show_helpers"] = show_helpers; + state["show_zoom_control"] = zoom_hb->is_visible(); state["show_edit_locks"] = show_edit_locks; state["snap_rotation"] = snap_rotation; state["snap_relative"] = snap_relative; @@ -4421,6 +4551,7 @@ Dictionary CanvasItemEditor::get_state() const { void CanvasItemEditor::set_state(const Dictionary &p_state) { + bool update_scrollbars = false; Dictionary state = p_state; if (state.has("zoom")) { zoom = p_state["zoom"]; @@ -4429,7 +4560,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { if (state.has("ofs")) { view_offset = p_state["ofs"]; previous_update_view_offset = view_offset; - _update_scrollbars(); + update_scrollbars = true; } if (state.has("grid_offset")) { @@ -4517,6 +4648,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { show_rulers = state["show_rulers"]; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); + update_scrollbars = true; } if (state.has("show_guides")) { @@ -4537,6 +4669,11 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { view_menu->get_popup()->set_item_checked(idx, show_edit_locks); } + if (state.has("show_zoom_control")) { + // This one is not user-controllable, but instrumentable + zoom_hb->set_visible(state["show_zoom_control"]); + } + if (state.has("snap_rotation")) { snap_rotation = state["snap_rotation"]; int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); @@ -4561,6 +4698,9 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); } + if (update_scrollbars) { + _update_scrollbars(); + } viewport->update(); } @@ -4636,6 +4776,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_relative = false; snap_pixel = false; + anchors_mode = false; + skeleton_show_bones = true; drag_type = DRAG_NONE; @@ -4644,6 +4786,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { dragged_guide_pos = Point2(); dragged_guide_index = -1; panning = false; + pan_pressed = false; bone_last_frame = 0; @@ -4654,6 +4797,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { editor_selection = p_editor->get_editor_selection(); editor_selection->add_editor_plugin(this); editor_selection->connect("selection_changed", this, "update"); + editor_selection->connect("selection_changed", this, "_selection_changed"); hb = memnew(HBoxContainer); add_child(hb); @@ -4913,6 +5057,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { anchors_popup->set_name("Anchors"); anchors_popup->connect("id_pressed", this, "_popup_callback"); + anchor_mode_button = memnew(ToolButton); + hb->add_child(anchor_mode_button); + anchor_mode_button->set_toggle_mode(true); + anchor_mode_button->hide(); + anchor_mode_button->connect("toggled", this, "_button_toggle_anchor_mode"); + animation_hb = memnew(HBoxContainer); hb->add_child(animation_hb); animation_hb->add_child(memnew(VSeparator)); @@ -4986,6 +5136,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY); divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE); + pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), KEY_SPACE); skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true); singleton = this; diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 14ea81f302..ff221eb758 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -129,6 +129,7 @@ private: ANCHORS_AND_MARGINS_PRESET_VCENTER_WIDE, ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE, ANCHORS_AND_MARGINS_PRESET_WIDE, + ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO, ANCHORS_PRESET_TOP_LEFT, ANCHORS_PRESET_TOP_RIGHT, ANCHORS_PRESET_BOTTOM_LEFT, @@ -240,6 +241,8 @@ private: Point2 view_offset; Point2 previous_update_view_offset; + bool anchors_mode; + Point2 grid_offset; Point2 grid_step; int grid_step_multiplier; @@ -262,6 +265,7 @@ private: bool key_rot; bool key_scale; bool panning; + bool pan_pressed; MenuOption last_option; @@ -347,6 +351,8 @@ private: PopupMenu *anchors_and_margins_popup; PopupMenu *anchors_popup; + ToolButton *anchor_mode_button; + Button *key_loc_button; Button *key_rot_button; Button *key_scale_button; @@ -378,6 +384,7 @@ private: Ref<ShortCut> set_pivot_shortcut; Ref<ShortCut> multiply_grid_step_shortcut; Ref<ShortCut> divide_grid_step_shortcut; + Ref<ShortCut> pan_view_shortcut; bool _is_node_locked(const Node *p_node); bool _is_node_movable(const Node *p_node, bool p_popup_warning = false); @@ -438,6 +445,7 @@ private: void _draw_guides(); void _draw_focus(); void _draw_grid(); + void _draw_control_anchors(Control *control); void _draw_control_helpers(Control *control); void _draw_selection(); void _draw_axis(); @@ -462,6 +470,8 @@ private: void _gui_input_viewport(const Ref<InputEvent> &p_event); + void _selection_changed(); + void _focus_selection(int p_op); void _solve_IK(Node2D *leaf_node, Point2 target_position); @@ -473,6 +483,9 @@ private: void _set_anchors_preset(Control::LayoutPreset p_preset); void _set_margins_preset(Control::LayoutPreset p_preset); void _set_anchors_and_margins_preset(Control::LayoutPreset p_preset); + void _set_anchors_and_margins_to_keep_ratio(); + + void _button_toggle_anchor_mode(bool p_status); HBoxContainer *zoom_hb; void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); @@ -575,6 +588,8 @@ public: void focus_selection(); + bool is_anchors_mode_enabled() { return anchors_mode; }; + CanvasItemEditor(EditorNode *p_editor); }; diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 55feb40c2c..3b1d728b8b 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -593,7 +593,8 @@ struct CanvasItemPlotCurve { color2(p_color2) {} void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) { - ci.draw_line(pos0, pos1, in_definition ? color1 : color2); + // FIXME: Using a line width greater than 1 breaks curve rendering + ci.draw_line(pos0, pos1, in_definition ? color1 : color2, 1, true); } }; @@ -616,8 +617,8 @@ void CurveEditor::_draw() { Vector2 min_edge = get_world_pos(Vector2(0, view_size.y)); Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0)); - const Color grid_color0 = Color(1.0, 1.0, 1.0, 0.15); - const Color grid_color1 = Color(1.0, 1.0, 1.0, 0.07); + const Color grid_color0 = get_color("mono_color", "Editor") * Color(1, 1, 1, 0.15); + const Color grid_color1 = get_color("mono_color", "Editor") * Color(1, 1, 1, 0.07); draw_line(Vector2(min_edge.x, curve.get_min_value()), Vector2(max_edge.x, curve.get_min_value()), grid_color0); draw_line(Vector2(max_edge.x, curve.get_max_value()), Vector2(min_edge.x, curve.get_max_value()), grid_color0); draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0); @@ -674,13 +675,13 @@ void CurveEditor::_draw() { if (i != 0) { Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT); - draw_line(get_view_pos(pos), control_pos, tangent_color); + draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE), true); draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color); } if (i != curve.get_point_count() - 1) { Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT); - draw_line(get_view_pos(pos), control_pos, tangent_color); + draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE), true); draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color); } } @@ -689,8 +690,8 @@ void CurveEditor::_draw() { draw_set_transform_matrix(_world_to_view); - const Color line_color = get_color("highlight_color", "Editor"); - const Color edge_line_color = get_color("font_color", "Editor"); + const Color line_color = get_color("font_color", "Editor"); + const Color edge_line_color = get_color("highlight_color", "Editor"); CanvasItemPlotCurve plot_func(*this, line_color, edge_line_color); plot_curve_accurate(curve, 4.f / view_size.x, plot_func); @@ -736,10 +737,10 @@ void CurveEditor::stroke_rect(Rect2 rect, Color color) { Vector2 c(rect.position.x, rect.position.y + rect.size.y); Vector2 d(rect.position + rect.size); - draw_line(a, b, color); - draw_line(b, d, color); - draw_line(d, c, color); - draw_line(c, a, color); + draw_line(a, b, color, Math::round(EDSCALE)); + draw_line(b, d, color, Math::round(EDSCALE)); + draw_line(d, c, color, Math::round(EDSCALE)); + draw_line(c, a, color, Math::round(EDSCALE)); } void CurveEditor::_bind_methods() { diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 58d7968723..285823d95a 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -78,7 +78,7 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -bool EditorTexturePreviewPlugin::should_generate_small_preview() const { +bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -186,7 +186,7 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 p EditorImagePreviewPlugin::EditorImagePreviewPlugin() { } -bool EditorImagePreviewPlugin::should_generate_small_preview() const { +bool EditorImagePreviewPlugin::generate_small_preview_automatically() const { return true; } //////////////////////////////////////////////////////////////////////////// @@ -250,7 +250,7 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2 return ptex; } -bool EditorBitmapPreviewPlugin::should_generate_small_preview() const { +bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -317,7 +317,7 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Material"); //any material } -bool EditorMaterialPreviewPlugin::should_generate_small_preview() const { +bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -643,7 +643,7 @@ Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from, const S float max = -1000; float min = 1000; int from = uint64_t(i) * frame_length / w; - int to = uint64_t(i + 1) * frame_length / w; + int to = (uint64_t(i) + 1) * frame_length / w; to = MIN(to, frame_length); from = MIN(from, frame_length - 1); if (to == from) { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index ed2c003a0a..16b1f3082b 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -39,7 +39,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorTexturePreviewPlugin(); @@ -49,7 +49,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorImagePreviewPlugin(); @@ -59,7 +59,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorBitmapPreviewPlugin(); @@ -98,7 +98,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMaterialPreviewPlugin(); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 18586b2fe5..e582f6ded2 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -71,6 +71,8 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, if (!p_merge) p_library->clear(); + Map<int, MeshInstance *> mesh_instances; + for (int i = 0; i < p_scene->get_child_count(); i++) { Node *child = p_scene->get_child(i); @@ -91,6 +93,15 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, if (mesh.is_null()) continue; + mesh = mesh->duplicate(); + for (int j = 0; j < mesh->get_surface_count(); ++j) { + Ref<Material> mat = mi->get_surface_material(j); + + if (mat.is_valid()) { + mesh->surface_set_material(j, mat); + } + } + int id = p_library->find_item_by_name(mi->get_name()); if (id < 0) { @@ -100,6 +111,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, } p_library->set_item_mesh(id, mesh); + mesh_instances[id] = mi; Vector<MeshLibrary::ShapeData> collisions; @@ -159,14 +171,26 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, if (1) { Vector<Ref<Mesh> > meshes; + Vector<Transform> transforms; Vector<int> ids = p_library->get_item_list(); for (int i = 0; i < ids.size(); i++) { - meshes.push_back(p_library->get_item_mesh(ids[i])); + + if (mesh_instances.find(ids[i])) { + + meshes.push_back(p_library->get_item_mesh(ids[i])); + transforms.push_back(mesh_instances[ids[i]]->get_transform()); + } } - Vector<Ref<Texture> > textures = EditorInterface::get_singleton()->make_mesh_previews(meshes, EditorSettings::get_singleton()->get("editors/grid_map/preview_size")); + Vector<Ref<Texture> > textures = EditorInterface::get_singleton()->make_mesh_previews(meshes, &transforms, EditorSettings::get_singleton()->get("editors/grid_map/preview_size")); + int j = 0; for (int i = 0; i < ids.size(); i++) { - p_library->set_item_preview(ids[i], textures[i]); + + if (mesh_instances.find(ids[i])) { + + p_library->set_item_preview(ids[i], textures[j]); + j++; + } } } } diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index fc0a425bfc..cae705a697 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -197,7 +197,7 @@ void MultiMeshEditor::_populate() { float areapos = Math::random(0.0f, area_accum); Map<float, int>::Element *E = triangle_area_map.find_closest(areapos); - ERR_FAIL_COND(!E) + ERR_FAIL_COND(!E); int index = E->get(); ERR_FAIL_INDEX(index, facecount); diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index 50bdf4512b..6fabdc93c6 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -93,7 +93,13 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { cpu_particles->set_pause_mode(particles->get_pause_mode()); cpu_particles->set_z_index(particles->get_z_index()); - EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(particles, cpu_particles, false); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to CPUParticles")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false); + ur->add_do_reference(particles); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false); + ur->add_undo_reference(this); + ur->commit_action(); } break; } diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 09180edf2a..3f4f66a26d 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -67,7 +67,7 @@ bool ParticlesEditorBase::_generate(PoolVector<Vector3> &points, PoolVector<Vect float areapos = Math::random(0.0f, area_accum); Map<float, int>::Element *E = triangle_area_map.find_closest(areapos); - ERR_FAIL_COND_V(!E, false) + ERR_FAIL_COND_V(!E, false); int index = E->get(); ERR_FAIL_INDEX_V(index, geometry.size(), false); @@ -312,7 +312,13 @@ void ParticlesEditor::_menu_option(int p_option) { cpu_particles->set_visible(node->is_visible()); cpu_particles->set_pause_mode(node->get_pause_mode()); - EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, cpu_particles, false); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to CPUParticles")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, cpu_particles, true, false); + ur->add_do_reference(node); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, node, false, false); + ur->add_undo_reference(this); + ur->commit_action(); } break; } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 0982da784f..b4719b2e6d 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -388,6 +388,14 @@ void ScriptEditor::_save_history() { void ScriptEditor::_go_to_tab(int p_idx) { + ScriptEditorBase *current = _get_current_editor(); + if (current) { + if (current->is_unsaved()) { + + current->apply_code(); + } + } + Control *c = Object::cast_to<Control>(tab_container->get_child(p_idx)); if (!c) return; @@ -429,6 +437,8 @@ void ScriptEditor::_go_to_tab(int p_idx) { if (script != NULL) { notify_script_changed(script); } + + Object::cast_to<ScriptEditorBase>(c)->validate(); } if (Object::cast_to<EditorHelp>(c)) { @@ -603,6 +613,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { idx = history[history_pos].control->get_index(); } tab_container->set_current_tab(idx); + } else { + _update_selected_editor_menu(); } _update_history_arrows(); @@ -713,6 +725,8 @@ void ScriptEditor::_resave_scripts(const String &p_str) { se->trim_trailing_whitespace(); } + se->insert_final_newline(); + if (convert_indent_on_save) { if (use_space_indentation) { se->convert_indent_to_spaces(); @@ -1002,6 +1016,9 @@ void ScriptEditor::_menu_option(int p_option) { save_all_scripts(); } break; + case SEARCH_IN_FILES: { + _on_find_in_files_requested(""); + } break; case SEARCH_HELP: { help_search_dialog->popup_dialog(); @@ -1065,6 +1082,8 @@ void ScriptEditor::_menu_option(int p_option) { if (trim_trailing_whitespace_on_save) current->trim_trailing_whitespace(); + current->insert_final_newline(); + if (convert_indent_on_save) { if (use_space_indentation) { current->convert_indent_to_spaces(); @@ -1084,7 +1103,10 @@ void ScriptEditor::_menu_option(int p_option) { } break; case FILE_SAVE_AS: { - current->trim_trailing_whitespace(); + if (trim_trailing_whitespace_on_save) + current->trim_trailing_whitespace(); + + current->insert_final_newline(); if (convert_indent_on_save) { if (use_space_indentation) { @@ -1227,7 +1249,7 @@ void ScriptEditor::_menu_option(int p_option) { if (p_option >= WINDOW_SELECT_BASE) { tab_container->set_current_tab(p_option - WINDOW_SELECT_BASE); - script_list->select(p_option - WINDOW_SELECT_BASE); + _update_script_names(); } } } @@ -1291,23 +1313,29 @@ void ScriptEditor::_theme_option(int p_option) { EditorSettings::get_singleton()->load_text_editor_theme(); } break; case THEME_SAVE: { - if (!EditorSettings::get_singleton()->save_text_editor_theme()) { + if (EditorSettings::get_singleton()->is_default_text_editor_theme()) { + ScriptEditor::_show_save_theme_as_dialog(); + } else if (!EditorSettings::get_singleton()->save_text_editor_theme()) { editor->show_warning(TTR("Error while saving theme"), TTR("Error saving")); } } break; case THEME_SAVE_AS: { - file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); - file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); - file_dialog_option = THEME_SAVE_AS; - file_dialog->clear_filters(); - 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...")); + ScriptEditor::_show_save_theme_as_dialog(); } break; } } +void ScriptEditor::_show_save_theme_as_dialog() { + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_option = THEME_SAVE_AS; + file_dialog->clear_filters(); + 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...")); +} + void ScriptEditor::_tab_changed(int p_which) { ensure_select_current(); @@ -1348,6 +1376,8 @@ void ScriptEditor::_notification(int p_what) { script_forward->set_icon(get_icon("Forward", "EditorIcons")); script_back->set_icon(get_icon("Back", "EditorIcons")); members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons")); + filter_scripts->set_right_icon(get_icon("Search", "EditorIcons")); + filter_methods->set_right_icon(get_icon("Search", "EditorIcons")); } break; case NOTIFICATION_READY: { @@ -1553,7 +1583,15 @@ struct _ScriptEditorItemData { bool operator<(const _ScriptEditorItemData &id) const { - return category == id.category ? sort_key < id.sort_key : category < id.category; + if (category == id.category) { + if (sort_key == id.sort_key) { + return index < id.index; + } else { + return sort_key < id.sort_key; + } + } else { + return category < id.category; + } } }; @@ -1597,8 +1635,12 @@ void ScriptEditor::_update_members_overview() { } for (int i = 0; i < functions.size(); i++) { - members_overview->add_item(functions[i].get_slice(":", 0)); - members_overview->set_item_metadata(i, functions[i].get_slice(":", 1).to_int() - 1); + String filter = filter_methods->get_text(); + String name = functions[i].get_slice(":", 0); + if (filter == "" || filter.is_subsequence_ofi(name)) { + members_overview->add_item(name); + members_overview->set_item_metadata(members_overview->get_item_count() - 1, functions[i].get_slice(":", 1).to_int() - 1); + } } String path = se->get_edited_resource()->get_path(); @@ -1720,7 +1762,19 @@ void ScriptEditor::_update_script_names() { Ref<Texture> icon = se->get_icon(); String path = se->get_edited_resource()->get_path(); bool built_in = !path.is_resource_file(); - String name = built_in ? path.get_file() : se->get_name(); + String name; + + if (built_in) { + + name = path.get_file(); + String resource_name = se->get_edited_resource()->get_name(); + if (resource_name != "") { + name = name.substr(0, name.find("::", 0) + 2) + resource_name; + } + } else { + + name = se->get_name(); + } _ScriptEditorItemData sd; sd.icon = icon; @@ -1805,20 +1859,26 @@ void ScriptEditor::_update_script_names() { _sort_list_on_update = false; } + Vector<_ScriptEditorItemData> sedata_filtered; for (int i = 0; i < sedata.size(); i++) { + String filter = filter_scripts->get_text(); + if (filter == "" || filter.is_subsequence_ofi(sedata[i].name)) { + sedata_filtered.push_back(sedata[i]); + } + } - script_list->add_item(sedata[i].name, sedata[i].icon); + for (int i = 0; i < sedata_filtered.size(); i++) { + script_list->add_item(sedata_filtered[i].name, sedata_filtered[i].icon); int index = script_list->get_item_count() - 1; - script_list->set_item_tooltip(index, sedata[i].tooltip); - script_list->set_item_metadata(index, sedata[i].index); - if (sedata[i].used) { - + script_list->set_item_tooltip(index, sedata_filtered[i].tooltip); + script_list->set_item_metadata(index, sedata_filtered[i].index); /* Saving as metadata the script's index in the tab container and not the filtered one */ + if (sedata_filtered[i].used) { script_list->set_item_custom_bg_color(index, Color(88 / 255.0, 88 / 255.0, 60 / 255.0)); } - if (tab_container->get_current_tab() == sedata[i].index) { + if (tab_container->get_current_tab() == sedata_filtered[i].index) { script_list->select(index); - script_name_label->set_text(sedata[i].name); - script_icon->set_texture(sedata[i].icon); + script_name_label->set_text(sedata_filtered[i].name); + script_icon->set_texture(sedata_filtered[i].icon); } } @@ -1936,10 +1996,11 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags"); List<String> args; + bool has_file_flag = false; + String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path()); if (flags.size()) { String project_path = ProjectSettings::get_singleton()->get_resource_path(); - String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path()); flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0)); flags = flags.replacen("{col}", itos(p_col)); @@ -1961,6 +2022,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra } else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) { String arg = flags.substr(from, num_chars); + if (arg.find("{file}") != -1) { + has_file_flag = true; + } // do path replacement here, else there will be issues with spaces and quotes arg = arg.replacen("{project}", project_path); @@ -1975,6 +2039,11 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra } } + // Default to passing script path if no {file} flag is specified. + if (!has_file_flag) { + args.push_back(script_path); + } + Error err = OS::get_singleton()->execute(path, args, false); if (err == OK) return false; @@ -1992,7 +2061,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra if (should_open) { if (tab_container->get_current_tab() != i) { _go_to_tab(i); - script_list->select(script_list->find_metadata(i)); + _update_script_names(); } if (is_visible_in_tree()) se->ensure_focus(); @@ -2001,6 +2070,8 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra se->goto_line(p_line - 1); } } + _update_script_names(); + script_list->ensure_current_is_visible(); return true; } } @@ -2088,6 +2159,8 @@ void ScriptEditor::save_all_scripts() { se->trim_trailing_whitespace(); } + se->insert_final_newline(); + if (!se->is_unsaved()) continue; @@ -2366,7 +2439,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node); EditorHelp *eh = Object::cast_to<EditorHelp>(node); if (se || eh) { - int new_index = script_list->get_item_at_position(p_point); + int new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point)); tab_container->move_child(node, new_index); tab_container->set_current_tab(new_index); _update_script_names(); @@ -2383,7 +2456,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node); EditorHelp *eh = Object::cast_to<EditorHelp>(node); if (se || eh) { - int new_index = script_list->get_item_at_position(p_point); + int new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point)); tab_container->move_child(node, new_index); tab_container->set_current_tab(new_index); _update_script_names(); @@ -2394,7 +2467,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co Vector<String> files = d["files"]; - int new_index = script_list->get_item_at_position(p_point); + int new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point)); int num_tabs_before = tab_container->get_child_count(); for (int i = 0; i < files.size(); i++) { String file = files[i]; @@ -2406,7 +2479,7 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co if (tab_container->get_child_count() > num_tabs_before) { tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index); num_tabs_before = tab_container->get_child_count(); - } else { + } else { /* Maybe script was already open */ tab_container->move_child(tab_container->get_child(tab_container->get_current_tab()), new_index); } } @@ -2694,10 +2767,22 @@ void ScriptEditor::_update_selected_editor_menu() { } EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control()); + script_search_menu->get_popup()->clear(); if (eh) { + + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); + script_search_menu->get_popup()->add_separator(); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); script_search_menu->show(); } else { - script_search_menu->hide(); + + if (tab_container->get_child_count() == 0) { + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); + script_search_menu->show(); + } else { + script_search_menu->hide(); + } } } @@ -2889,6 +2974,14 @@ void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) { _update_modified_scripts_for_external_editor(); } +void ScriptEditor::_filter_scripts_text_changed(const String &p_newtext) { + _update_script_names(); +} + +void ScriptEditor::_filter_methods_text_changed(const String &p_newtext) { + _update_members_overview(); +} + void ScriptEditor::_bind_methods() { ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action); @@ -2940,6 +3033,8 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_toggle_members_overview_alpha_sort", &ScriptEditor::_toggle_members_overview_alpha_sort); ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview); ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed); + ClassDB::bind_method("_filter_scripts_text_changed", &ScriptEditor::_filter_scripts_text_changed); + ClassDB::bind_method("_filter_methods_text_changed", &ScriptEditor::_filter_methods_text_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); @@ -2986,8 +3081,18 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_split->add_child(list_split); list_split->set_v_size_flags(SIZE_EXPAND_FILL); + scripts_vbox = memnew(VBoxContainer); + scripts_vbox->set_v_size_flags(SIZE_EXPAND_FILL); + list_split->add_child(scripts_vbox); + + filter_scripts = memnew(LineEdit); + filter_scripts->set_placeholder(TTR("Filter scripts")); + filter_scripts->set_clear_button_enabled(true); + filter_scripts->connect("text_changed", this, "_filter_scripts_text_changed"); + scripts_vbox->add_child(filter_scripts); + script_list = memnew(ItemList); - list_split->add_child(script_list); + scripts_vbox->add_child(script_list); script_list->set_custom_minimum_size(Size2(150, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing script_list->set_v_size_flags(SIZE_EXPAND_FILL); script_split->set_split_offset(140); @@ -3023,6 +3128,12 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { buttons_hbox->add_child(members_overview_alphabeta_sort_button); + filter_methods = memnew(LineEdit); + filter_methods->set_placeholder(TTR("Filter methods")); + filter_methods->set_clear_button_enabled(true); + filter_methods->connect("text_changed", this, "_filter_methods_text_changed"); + overview_vbox->add_child(filter_methods); + members_overview = memnew(ItemList); overview_vbox->add_child(members_overview); @@ -3030,7 +3141,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { members_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing members_overview->set_v_size_flags(SIZE_EXPAND_FILL); members_overview->set_allow_rmb_select(true); - members_overview->set_drag_forwarding(this); help_overview = memnew(ItemList); overview_vbox->add_child(help_overview); @@ -3109,10 +3219,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu->set_text(TTR("Search")); script_search_menu->set_switch_on_hover(true); script_search_menu->get_popup()->set_hide_on_window_lose_focus(true); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); - script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); - script_search_menu->hide(); debug_menu = memnew(MenuButton); menu_hb->add_child(debug_menu); @@ -3291,9 +3398,7 @@ void ScriptEditorPlugin::edit(Object *p_object) { } else { script_editor->edit(p_script); } - } - - if (Object::cast_to<TextFile>(p_object)) { + } else if (Object::cast_to<TextFile>(p_object)) { script_editor->edit(Object::cast_to<TextFile>(p_object)); } } @@ -3389,7 +3494,8 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) { 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); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE)); - EDITOR_DEF("text_editor/external/exec_flags", ""); + EDITOR_DEF("text_editor/external/exec_flags", "{file}"); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}.")); ED_SHORTCUT("script_editor/open_recent", TTR("Open Recent"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T); ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files")); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index a17fed1e06..0d9168261a 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -38,6 +38,7 @@ #include "editor/editor_plugin.h" #include "editor/script_create_dialog.h" #include "scene/gui/item_list.h" +#include "scene/gui/line_edit.h" #include "scene/gui/menu_button.h" #include "scene/gui/split_container.h" #include "scene/gui/tab_container.h" @@ -99,6 +100,7 @@ public: virtual void set_executing_line(int p_line) = 0; virtual void clear_executing_line() = 0; virtual void trim_trailing_whitespace() = 0; + virtual void insert_final_newline() = 0; virtual void convert_indent_to_spaces() = 0; virtual void convert_indent_to_tabs() = 0; virtual void ensure_focus() = 0; @@ -116,6 +118,8 @@ public: virtual Control *get_edit_menu() = 0; virtual void clear_edit_menu() = 0; + virtual void validate() = 0; + ScriptEditorBase() {} }; @@ -157,6 +161,7 @@ class ScriptEditor : public PanelContainer { DEBUG_SHOW, DEBUG_SHOW_KEEP_OPEN, DEBUG_WITH_EXTERNAL_EDITOR, + SEARCH_IN_FILES, SEARCH_HELP, SEARCH_WEBSITE, REQUEST_DOCS, @@ -209,6 +214,9 @@ class ScriptEditor : public PanelContainer { ItemList *script_list; HSplitContainer *script_split; ItemList *members_overview; + LineEdit *filter_scripts; + LineEdit *filter_methods; + VBoxContainer *scripts_vbox; VBoxContainer *overview_vbox; HBoxContainer *buttons_hbox; Label *filename; @@ -262,6 +270,7 @@ class ScriptEditor : public PanelContainer { void _tab_changed(int p_which); void _menu_option(int p_option); void _theme_option(int p_option); + void _show_save_theme_as_dialog(); Tree *disk_changed_list; ConfirmationDialog *disk_changed; @@ -336,6 +345,8 @@ class ScriptEditor : public PanelContainer { void _update_members_overview_visibility(); void _update_members_overview(); void _toggle_members_overview_alpha_sort(bool p_alphabetic_sort); + void _filter_scripts_text_changed(const String &p_newtext); + void _filter_methods_text_changed(const String &p_newtext); void _update_script_names(); void _update_script_connections(); bool _sort_list_on_update; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index d40e67cc8c..ce0859a1f6 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -213,6 +213,7 @@ void ScriptTextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); @@ -245,6 +246,7 @@ void ScriptTextEditor::_load_theme_settings() { text_edit->add_color_override("number_color", number_color); text_edit->add_color_override("function_color", function_color); text_edit->add_color_override("member_variable_color", member_variable_color); + text_edit->add_color_override("bookmark_color", bookmark_color); text_edit->add_color_override("breakpoint_color", breakpoint_color); text_edit->add_color_override("executing_line_color", executing_line_color); text_edit->add_color_override("mark_color", mark_color); @@ -376,7 +378,6 @@ void ScriptTextEditor::reload_text() { int v = te->get_v_scroll(); te->set_text(script->get_source_code()); - te->clear_undo_history(); te->cursor_set_line(row); te->cursor_set_column(column); te->set_h_scroll(h); @@ -456,6 +457,11 @@ void ScriptTextEditor::trim_trailing_whitespace() { code_editor->trim_trailing_whitespace(); } +void ScriptTextEditor::insert_final_newline() { + + code_editor->insert_final_newline(); +} + void ScriptTextEditor::convert_indent_to_spaces() { code_editor->convert_indent_to_spaces(); @@ -541,7 +547,6 @@ void ScriptTextEditor::_validate_script() { script->set_source_code(text); script->update_exports(); _update_member_keywords(); - //script->reload(); //will update all the variables in property editors } functions.clear(); @@ -552,30 +557,36 @@ void ScriptTextEditor::_validate_script() { } _update_connected_methods(); - code_editor->set_warning_nb(missing_connections.size() + warnings.size()); + int warning_nb = warnings.size(); warnings_panel->clear(); - // add missing connections - Node *base = get_tree()->get_edited_scene_root(); - if (base && missing_connections.size() > 0) { - warnings_panel->push_table(1); - for (List<Connection>::Element *E = missing_connections.front(); E; E = E->next()) { - Connection connection = E->get(); - - String base_path = base->get_name(); - String source_path = base == connection.source ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.source))); - String target_path = base == connection.target ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.target))); + // Add missing connections. + if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) { + Node *base = get_tree()->get_edited_scene_root(); + if (base && missing_connections.size() > 0) { + warnings_panel->push_table(1); + for (List<Connection>::Element *E = missing_connections.front(); E; E = E->next()) { + Connection connection = E->get(); + + String base_path = base->get_name(); + String source_path = base == connection.source ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.source))); + String target_path = base == connection.target ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.target))); + + warnings_panel->push_cell(); + warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor")); + warnings_panel->add_text(vformat(TTR("Missing connected method '%s' for signal '%s' from node '%s' to node '%s'."), connection.method, connection.signal, source_path, target_path)); + warnings_panel->pop(); // Color. + warnings_panel->pop(); // Cell. + } + warnings_panel->pop(); // Table. - warnings_panel->push_cell(); - warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor")); - warnings_panel->add_text(vformat(TTR("Missing connected method '%s' for signal '%s' from node '%s' to node '%s'"), connection.method, connection.signal, source_path, target_path)); - warnings_panel->pop(); // Color - warnings_panel->pop(); // Cell + warning_nb += missing_connections.size(); } - warnings_panel->pop(); // Table } - // add script warnings + code_editor->set_warning_nb(warning_nb); + + // Add script warnings. warnings_panel->push_table(3); for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) { ScriptLanguage::Warning w = E->get(); @@ -585,13 +596,13 @@ void ScriptTextEditor::_validate_script() { warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor")); warnings_panel->add_text(TTR("Line") + " " + itos(w.line)); warnings_panel->add_text(" (" + w.string_code + "):"); - warnings_panel->pop(); // Color - warnings_panel->pop(); // Meta goto - warnings_panel->pop(); // Cell + warnings_panel->pop(); // Color. + warnings_panel->pop(); // Meta goto. + warnings_panel->pop(); // Cell. warnings_panel->push_cell(); warnings_panel->add_text(w.message); - warnings_panel->pop(); // Cell + warnings_panel->pop(); // Cell. Dictionary ignore_meta; ignore_meta["line"] = w.line; @@ -599,11 +610,10 @@ void ScriptTextEditor::_validate_script() { warnings_panel->push_cell(); warnings_panel->push_meta(ignore_meta); warnings_panel->add_text(TTR("(ignore)")); - warnings_panel->pop(); // Meta ignore - warnings_panel->pop(); // Cell - //warnings_panel->add_newline(); + warnings_panel->pop(); // Meta ignore. + warnings_panel->pop(); // Cell. } - warnings_panel->pop(); // Table + warnings_panel->pop(); // Table. line--; bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true); @@ -734,7 +744,7 @@ void ScriptTextEditor::_code_complete_script(const String &p_code, List<String> base = _find_node_for_script(base, base, script); } String hint; - Error err = script->get_language()->complete_code(p_code, script->get_path().get_base_dir(), base, r_options, r_force, hint); + Error err = script->get_language()->complete_code(p_code, script->get_path(), base, r_options, r_force, hint); if (err == OK && hint != "") { code_editor->get_text_edit()->set_code_hint(hint); } @@ -765,7 +775,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c EditorNode::get_singleton()->load_resource(p_symbol); } - } else if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path().get_base_dir(), base, result) == OK) { + } else if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path(), base, result) == OK) { _goto_line(p_row); @@ -866,12 +876,36 @@ void ScriptTextEditor::_update_connected_methods() { continue; } - int line = script->get_language()->find_function(connection.method, text_edit->get_text()); - if (line < 0) { - missing_connections.push_back(connection); + // As deleted nodes are still accessible via the undo/redo system, check if they're still on the tree. + Node *source = Object::cast_to<Node>(connection.source); + if (source && !source->is_inside_tree()) { continue; } - text_edit->set_line_info_icon(line - 1, get_parent_control()->get_icon("Slot", "EditorIcons"), connection.method); + + if (!ClassDB::has_method(script->get_instance_base_type(), connection.method)) { + + int line = script->get_language()->find_function(connection.method, text_edit->get_text()); + if (line < 0) { + // There is a chance that the method is inherited from another script. + bool found_inherited_function = false; + Ref<Script> inherited_script = script->get_base_script(); + while (!inherited_script.is_null()) { + line = inherited_script->get_language()->find_function(connection.method, inherited_script->get_source_code()); + if (line != -1) { + found_inherited_function = true; + break; + } + + inherited_script = inherited_script->get_base_script(); + } + + if (!found_inherited_function) { + missing_connections.push_back(connection); + } + } else { + text_edit->set_line_info_icon(line - 1, get_parent_control()->get_icon("Slot", "EditorIcons"), connection.method); + } + } } } } @@ -1066,6 +1100,22 @@ void ScriptTextEditor::_edit_option(int p_op) { goto_line_dialog->popup_find_line(tx); } break; + case BOOKMARK_TOGGLE: { + + code_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + code_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + code_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + code_editor->remove_all_bookmarks(); + } break; case DEBUG_TOGGLE_BREAKPOINT: { int line = tx->cursor_get_line(); @@ -1266,7 +1316,8 @@ bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_ Dictionary d = p_data; if (d.has("type") && (String(d["type"]) == "resource" || String(d["type"]) == "files" || - String(d["type"]) == "nodes")) { + String(d["type"]) == "nodes" || + String(d["type"]) == "files_and_dirs")) { return true; } @@ -1328,7 +1379,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->insert_text_at_cursor(res->get_path()); } - if (d.has("type") && String(d["type"]) == "files") { + if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { Array files = d["files"]; @@ -1429,7 +1480,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { base = _find_node_for_script(base, base, script); } ScriptLanguage::LookupResult result; - if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_mouse, script->get_path().get_base_dir(), base, result) == OK) { + if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_mouse, script->get_path(), base, result) == OK) { open_docs = true; } } @@ -1499,6 +1550,7 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); if (p_selection) { context_menu->add_separator(); @@ -1651,6 +1703,16 @@ ScriptTextEditor::ScriptTextEditor() { search_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + edit_hb->add_child(edit_menu); quick_open = memnew(ScriptEditorQuickOpen); @@ -1692,6 +1754,10 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), 0); ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0); ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K); + ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_B); + ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KEY_MASK_CMD | KEY_B); + ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B); + ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), 0); ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F); ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), 0); ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); @@ -1699,7 +1765,7 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else - ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); + ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_D); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); @@ -1739,8 +1805,12 @@ void ScriptTextEditor::register_editor() { #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE); #else - ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1); + ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_F1); #endif ScriptEditor::register_create_script_editor_function(create_editor); } + +void ScriptTextEditor::validate() { + this->code_editor->validate_script(); +} diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 0dbc884594..24d40a5eec 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -128,6 +128,10 @@ class ScriptTextEditor : public ScriptEditorBase { SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, SEARCH_IN_FILES, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, DEBUG_TOGGLE_BREAKPOINT, DEBUG_REMOVE_ALL_BREAKPOINTS, DEBUG_GOTO_NEXT_BREAKPOINT, @@ -190,6 +194,7 @@ public: virtual void set_edit_state(const Variant &p_state); virtual void ensure_focus(); virtual void trim_trailing_whitespace(); + virtual void insert_final_newline(); virtual void convert_indent_to_spaces(); virtual void convert_indent_to_tabs(); virtual void tag_saved_version(); @@ -215,6 +220,8 @@ public: virtual void clear_edit_menu(); static void register_editor(); + virtual void validate(); + ScriptTextEditor(); }; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 31660a9e19..f9ca38b919 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -60,6 +60,26 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) { _line_col_changed(); } +void ShaderTextEditor::reload_text() { + ERR_FAIL_COND(shader.is_null()); + + TextEdit *te = get_text_edit(); + int column = te->cursor_get_column(); + int row = te->cursor_get_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(shader->get_code()); + te->cursor_set_line(row); + te->cursor_set_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + + te->tag_saved_version(); + + update_line_and_column(); +} + void ShaderTextEditor::_load_theme_settings() { get_text_edit()->clear_colors(); @@ -84,6 +104,7 @@ void ShaderTextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); @@ -113,6 +134,7 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_color_override("function_color", function_color); get_text_edit()->add_color_override("member_variable_color", member_variable_color); get_text_edit()->add_color_override("mark_color", mark_color); + get_text_edit()->add_color_override("bookmark_color", bookmark_color); get_text_edit()->add_color_override("breakpoint_color", breakpoint_color); get_text_edit()->add_color_override("executing_line_color", executing_line_color); get_text_edit()->add_color_override("code_folding_color", code_folding_color); @@ -304,6 +326,22 @@ void ShaderEditor::_menu_option(int p_option) { goto_line_dialog->popup_find_line(shader_editor->get_text_edit()); } break; + case BOOKMARK_TOGGLE: { + + shader_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + shader_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + shader_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + shader_editor->remove_all_bookmarks(); + } break; } if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) { shader_editor->get_text_edit()->call_deferred("grab_focus"); @@ -312,9 +350,8 @@ void ShaderEditor::_menu_option(int p_option) { void ShaderEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - //if (is_visible_in_tree()) - // shader_editor->get_text_edit()->grab_focus(); + if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_IN) { + _check_for_external_edit(); } } @@ -345,12 +382,14 @@ void ShaderEditor::_editor_settings_changed() { void ShaderEditor::_bind_methods() { + ClassDB::bind_method("_reload_shader_from_disk", &ShaderEditor::_reload_shader_from_disk); ClassDB::bind_method("_editor_settings_changed", &ShaderEditor::_editor_settings_changed); ClassDB::bind_method("_text_edit_gui_input", &ShaderEditor::_text_edit_gui_input); ClassDB::bind_method("_menu_option", &ShaderEditor::_menu_option); ClassDB::bind_method("_params_changed", &ShaderEditor::_params_changed); ClassDB::bind_method("apply_shaders", &ShaderEditor::apply_shaders); + ClassDB::bind_method("save_external_data", &ShaderEditor::save_external_data); } void ShaderEditor::ensure_select_current() { @@ -371,6 +410,37 @@ void ShaderEditor::goto_line_selection(int p_line, int p_begin, int p_end) { shader_editor->goto_line_selection(p_line, p_begin, p_end); } +void ShaderEditor::_check_for_external_edit() { + + if (shader.is_null() || !shader.is_valid()) { + return; + } + + // internal shader. + if (shader->get_path() == "" || shader->get_path().find("local://") != -1 || shader->get_path().find("::") != -1) { + return; + } + + bool use_autoreload = bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", false)); + if (shader->get_last_modified_time() != FileAccess::get_modified_time(shader->get_path())) { + if (use_autoreload) { + _reload_shader_from_disk(); + } else { + disk_changed->call_deferred("popup_centered"); + } + } +} + +void ShaderEditor::_reload_shader_from_disk() { + + Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), true); + ERR_FAIL_COND(!rel_shader.is_valid()); + + shader->set_code(rel_shader->get_code()); + shader->set_last_modified_time(rel_shader->get_last_modified_time()); + shader_editor->reload_text(); +} + void ShaderEditor::edit(const Ref<Shader> &p_shader) { if (p_shader.is_null() || !p_shader->is_text_shader()) @@ -387,16 +457,20 @@ void ShaderEditor::edit(const Ref<Shader> &p_shader) { // see if already has it } -void ShaderEditor::save_external_data() { +void ShaderEditor::save_external_data(const String &p_str) { - if (shader.is_null()) + if (shader.is_null()) { + disk_changed->hide(); return; - apply_shaders(); + } + apply_shaders(); if (shader->get_path() != "" && shader->get_path().find("local://") == -1 && shader->get_path().find("::") == -1) { //external shader, save it ResourceSaver::save(shader->get_path(), shader); } + + disk_changed->hide(); } void ShaderEditor::apply_shaders() { @@ -535,6 +609,16 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + add_child(main_container); main_container->add_child(hbc); hbc->add_child(search_menu); @@ -545,6 +629,23 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); + disk_changed = memnew(ConfirmationDialog); + + VBoxContainer *vbc = memnew(VBoxContainer); + disk_changed->add_child(vbc); + + Label *dl = memnew(Label); + dl->set_text(TTR("This shader has been modified on on disk.\nWhat action should be taken?")); + vbc->add_child(dl); + + disk_changed->connect("confirmed", this, "_reload_shader_from_disk"); + disk_changed->get_ok()->set_text(TTR("Reload")); + + disk_changed->add_button(TTR("Resave"), !OS::get_singleton()->get_swap_ok_cancel(), "resave"); + disk_changed->connect("custom_action", this, "save_external_data"); + + add_child(disk_changed); + _editor_settings_changed(); } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 46c78c1d33..b56c1451ad 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -58,6 +58,8 @@ protected: public: virtual void _validate_script(); + void reload_text(); + Ref<Shader> get_edited_shader() const; void set_edited_shader(const Ref<Shader> &p_shader); ShaderTextEditor(); @@ -88,6 +90,10 @@ class ShaderEditor : public PanelContainer { SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, }; @@ -99,6 +105,7 @@ class ShaderEditor : public PanelContainer { GotoLineDialog *goto_line_dialog; ConfirmationDialog *erase_tab_confirm; + ConfirmationDialog *disk_changed; ShaderTextEditor *shader_editor; @@ -108,6 +115,9 @@ class ShaderEditor : public PanelContainer { void _editor_settings_changed(); + void _check_for_external_edit(); + void _reload_shader_from_disk(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -123,7 +133,7 @@ public: void goto_line_selection(int p_line, int p_begin, int p_end); virtual Size2 get_minimum_size() const { return Size2(0, 200); } - void save_external_data(); + void save_external_data(const String &p_str = ""); ShaderEditor(EditorNode *p_node); }; diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 5a733f6509..a1c0b732fa 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -3510,10 +3510,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed camera->make_current(); surface->set_focus_mode(FOCUS_ALL); + VBoxContainer *vbox = memnew(VBoxContainer); + surface->add_child(vbox); + vbox->set_position(Point2(10, 10) * EDSCALE); + view_menu = memnew(MenuButton); view_menu->set_flat(false); - surface->add_child(view_menu); - view_menu->set_position(Point2(10, 10) * EDSCALE); + vbox->add_child(view_menu); + view_menu->set_h_size_flags(0); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); @@ -3566,9 +3570,9 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); preview_camera = memnew(CheckBox); - preview_camera->set_position(Point2(10, 38) * EDSCALE); // Below the 'view_menu' MenuButton. preview_camera->set_text(TTR("Preview")); - surface->add_child(preview_camera); + vbox->add_child(preview_camera); + preview_camera->set_h_size_flags(0); preview_camera->hide(); preview_camera->connect("toggled", this, "_toggle_camera_preview"); previewing = NULL; @@ -4207,7 +4211,7 @@ void SpatialEditor::set_state(const Dictionary &p_state) { Array vp = d["viewports"]; uint32_t vp_size = static_cast<uint32_t>(vp.size()); if (vp_size > VIEWPORTS_COUNT) { - WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state.") + WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state."); vp_size = VIEWPORTS_COUNT; } @@ -5605,7 +5609,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1); ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3); ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3); - ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal view"), KEY_KP_5); + ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5); ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K); ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O); ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F); @@ -5626,7 +5630,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(transform_menu); p = transform_menu->get_popup(); - p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap object to floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); + p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR); p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 7642bfaf04..2deb2090e2 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -327,7 +327,14 @@ void SpriteEditor::_convert_to_mesh_2d_node() { MeshInstance2D *mesh_instance = memnew(MeshInstance2D); mesh_instance->set_mesh(mesh); - EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance); + + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to Mesh2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, mesh_instance, true, false); + ur->add_do_reference(mesh_instance); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", mesh_instance, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); } void SpriteEditor::_convert_to_polygon_2d_node() { @@ -379,7 +386,13 @@ void SpriteEditor::_convert_to_polygon_2d_node() { polygon_2d_instance->set_polygon(polygon); polygon_2d_instance->set_polygons(polys); - EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, polygon_2d_instance); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Convert to Polygon2D")); + ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", node, polygon_2d_instance, true, false); + ur->add_do_reference(polygon_2d_instance); + ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", polygon_2d_instance, node, false, false); + ur->add_undo_reference(node); + ur->commit_action(); } void SpriteEditor::_create_collision_polygon_2d_node() { @@ -396,7 +409,12 @@ void SpriteEditor::_create_collision_polygon_2d_node() { CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); collision_polygon_2d_instance->set_polygon(outline); - _add_as_sibling_or_child(node, collision_polygon_2d_instance); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create CollisionPolygon2D Sibling")); + ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance); + ur->add_do_reference(collision_polygon_2d_instance); + ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", collision_polygon_2d_instance); + ur->commit_action(); } } @@ -425,15 +443,20 @@ void SpriteEditor::_create_light_occluder_2d_node() { LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); light_occluder_2d_instance->set_occluder_polygon(polygon); - _add_as_sibling_or_child(node, light_occluder_2d_instance); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Create LightOccluder2D Sibling")); + ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance); + ur->add_do_reference(light_occluder_2d_instance); + ur->add_undo_method(node != this->get_tree()->get_edited_scene_root() ? node->get_parent() : this->get_tree()->get_edited_scene_root(), "remove_child", light_occluder_2d_instance); + ur->commit_action(); } } -void SpriteEditor::_add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node) { +void SpriteEditor::_add_as_sibling_or_child(Node *p_own_node, Node *p_new_node) { // Can't make sibling if own node is scene root if (p_own_node != this->get_tree()->get_edited_scene_root()) { p_own_node->get_parent()->add_child(p_new_node, true); - p_new_node->set_transform(p_own_node->get_transform()); + Object::cast_to<Node2D>(p_new_node)->set_transform(Object::cast_to<Node2D>(p_own_node)->get_transform()); } else { p_own_node->add_child(p_new_node, true); } @@ -534,6 +557,7 @@ void SpriteEditor::_bind_methods() { ClassDB::bind_method("_debug_uv_draw", &SpriteEditor::_debug_uv_draw); ClassDB::bind_method("_update_mesh_data", &SpriteEditor::_update_mesh_data); ClassDB::bind_method("_create_node", &SpriteEditor::_create_node); + ClassDB::bind_method("_add_as_sibling_or_child", &SpriteEditor::_add_as_sibling_or_child); } SpriteEditor::SpriteEditor() { diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 460f5a5707..81be4a19e9 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -84,7 +84,7 @@ class SpriteEditor : public Control { void _create_collision_polygon_2d_node(); void _create_light_occluder_2d_node(); - void _add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node); + void _add_as_sibling_or_child(Node *p_own_node, Node *p_new_node); protected: void _node_removed(Node *p_node); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 6edd19901b..c509202a88 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -51,32 +51,42 @@ void SpriteFramesEditor::_open_sprite_sheet() { } void SpriteFramesEditor::_sheet_preview_draw() { + Size2i size = split_sheet_preview->get_size(); int h = split_sheet_h->get_value(); int v = split_sheet_v->get_value(); + int width = size.width / h; + int height = size.height / v; const float a = 0.3; for (int i = 1; i < h; i++) { - for (int j = 1; j < v; j++) { - int x = i * size.width / h; - int y = j * size.height / v; + int x = i * width; + split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a)); + split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a)); - split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a)); - split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a)); + for (int j = 1; j < v; j++) { + + int y = j * height; split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a)); split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a)); } } + if (frames_selected.size() == 0) { + split_sheet_dialog->get_ok()->set_disabled(true); + split_sheet_dialog->get_ok()->set_text(TTR("No Frames Selected")); + return; + } + Color accent = get_color("accent_color", "Editor"); for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { int idx = E->get(); - int x = (idx % h) * size.width / h; - int y = (idx / v) * size.height / v; - int width = size.width / h; - int height = size.height / v; + int xp = idx % h; + int yp = (idx - xp) / h; + int x = xp * width; + int y = yp * height; split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true); split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false); @@ -87,13 +97,8 @@ void SpriteFramesEditor::_sheet_preview_draw() { split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false); } - if (frames_selected.size() == 0) { - split_sheet_dialog->get_ok()->set_disabled(true); - split_sheet_dialog->get_ok()->set_text(TTR("No Frames Selected")); - } else { - split_sheet_dialog->get_ok()->set_disabled(false); - split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d Frame(s)"), frames_selected.size())); - } + split_sheet_dialog->get_ok()->set_disabled(false); + split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d Frame(s)"), frames_selected.size())); } void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { @@ -149,10 +154,12 @@ void SpriteFramesEditor::_sheet_add_frames() { for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { int idx = E->get(); - int x = (idx % h) * size.width / h; - int y = (idx / v) * size.height / v; int width = size.width / h; int height = size.height / v; + int xp = idx % h; + int yp = (idx - xp) / h; + int x = xp * width; + int y = yp * height; Ref<AtlasTexture> at; at.instance(); @@ -456,6 +463,10 @@ void SpriteFramesEditor::_animation_select() { if (updating) return; + double value = anim_speed->get_line_edit()->get_text().to_double(); + if (!Math::is_equal_approx(value, frames->get_animation_speed(edited_anim))) + _animation_fps_changed(value); + TreeItem *selected = animations->get_selected(); ERR_FAIL_COND(!selected); edited_anim = selected->get_text(0); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index becaae3567..eeef3397d2 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -93,6 +93,7 @@ void TextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); @@ -127,6 +128,7 @@ void TextEditor::_load_theme_settings() { text_edit->add_color_override("breakpoint_color", breakpoint_color); text_edit->add_color_override("executing_line_color", executing_line_color); text_edit->add_color_override("mark_color", mark_color); + text_edit->add_color_override("bookmark_color", bookmark_color); text_edit->add_color_override("code_folding_color", code_folding_color); text_edit->add_color_override("search_result_color", search_result_color); text_edit->add_color_override("search_result_border_color", search_result_border_color); @@ -202,7 +204,6 @@ void TextEditor::reload_text() { int v = te->get_v_scroll(); te->set_text(text_file->get_text()); - te->clear_undo_history(); te->cursor_set_line(row); te->cursor_set_column(column); te->set_h_scroll(h); @@ -250,6 +251,11 @@ void TextEditor::trim_trailing_whitespace() { code_editor->trim_trailing_whitespace(); } +void TextEditor::insert_final_newline() { + + code_editor->insert_final_newline(); +} + void TextEditor::convert_indent_to_spaces() { code_editor->convert_indent_to_spaces(); @@ -438,6 +444,22 @@ void TextEditor::_edit_option(int p_op) { goto_line_dialog->popup_find_line(tx); } break; + case BOOKMARK_TOGGLE: { + + code_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + code_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + code_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + code_editor->remove_all_bookmarks(); + } break; } } @@ -620,5 +642,18 @@ TextEditor::TextEditor() { highlighter_menu->add_radio_check_item(TTR("Standard")); highlighter_menu->connect("id_pressed", this, "_change_syntax_highlighter"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + code_editor->get_text_edit()->set_drag_forwarding(this); } + +void TextEditor::validate() { +} diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 767001e2f6..e91909e0ea 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -87,6 +87,10 @@ private: SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, }; protected: @@ -126,6 +130,7 @@ public: virtual void set_executing_line(int p_line); virtual void clear_executing_line(); virtual void trim_trailing_whitespace(); + virtual void insert_final_newline(); virtual void convert_indent_to_spaces(); virtual void convert_indent_to_tabs(); virtual void ensure_focus(); @@ -140,6 +145,8 @@ public: virtual Control *get_edit_menu(); virtual void clear_edit_menu(); + virtual void validate(); + static void register_editor(); TextEditor(); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 80e2e99685..5b67d259ba 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -631,12 +631,14 @@ ThemeEditor::ThemeEditor() { scroll = memnew(ScrollContainer); add_child(scroll); + scroll->set_theme(Theme::get_default()); scroll->set_enable_v_scroll(true); scroll->set_enable_h_scroll(false); scroll->set_v_size_flags(SIZE_EXPAND_FILL); main_container = memnew(MarginContainer); scroll->add_child(main_container); + main_container->set_theme(Theme::get_default()); main_container->set_clip_contents(true); main_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE); main_container->set_v_size_flags(SIZE_EXPAND_FILL); @@ -646,11 +648,9 @@ ThemeEditor::ThemeEditor() { Panel *panel = memnew(Panel); main_container->add_child(panel); - panel->set_theme(Theme::get_default()); MarginContainer *mc = memnew(MarginContainer); main_container->add_child(mc); - mc->set_theme(Theme::get_default()); mc->add_constant_override("margin_right", 4 * EDSCALE); mc->add_constant_override("margin_top", 4 * EDSCALE); mc->add_constant_override("margin_left", 4 * EDSCALE); @@ -683,7 +683,6 @@ ThemeEditor::ThemeEditor() { CheckButton *cb = memnew(CheckButton); cb->set_text("CheckButton"); first_vb->add_child(cb); - cb = memnew(CheckButton); CheckBox *cbx = memnew(CheckBox); cbx->set_text("CheckBox"); first_vb->add_child(cbx); @@ -880,11 +879,9 @@ ThemeEditor::ThemeEditor() { void ThemeEditorPlugin::edit(Object *p_node) { if (Object::cast_to<Theme>(p_node)) { - theme_editor->show(); theme_editor->edit(Object::cast_to<Theme>(p_node)); } else { theme_editor->edit(Ref<Theme>()); - theme_editor->hide(); } } @@ -899,11 +896,11 @@ void ThemeEditorPlugin::make_visible(bool p_visible) { theme_editor->set_process(true); button->show(); editor->make_bottom_panel_item_visible(theme_editor); - } else { theme_editor->set_process(false); if (theme_editor->is_visible_in_tree()) editor->hide_bottom_panel(); + button->hide(); } } diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 29a54f815d..16f93b8fd3 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -299,9 +299,13 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p Vector2 position; int current = manual_palette->get_current(); if (current != -1) { - position = manual_palette->get_item_metadata(current); + if (tool != TOOL_PASTING) { + position = manual_palette->get_item_metadata(current); + } else { + position = p_autotile_coord; + } } else { - // if there is no manual tile selected, that either means that + // If there is no manual tile selected, that either means that // autotiling is enabled, or the given tile is not autotiling. Either // way, the coordinate of the tile does not matter, so assigning it to // the coordinate of the existing tile works fine. @@ -309,7 +313,7 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p } if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose && prev_position == position) - return; //check that it's actually different + return; // Check that it's actually different. for (int y = p_pos.y - 1; y <= p_pos.y + 1; y++) { for (int x = p_pos.x - 1; x <= p_pos.x + 1; x++) { @@ -322,15 +326,19 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p node->_set_celld(p_pos, _create_cell_dictionary(p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord)); + if (tool == TOOL_PASTING) + return; + if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) { if (current != -1) { node->set_cell_autotile_coord(p_pos.x, p_pos.y, position); + } else if (node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE && priority_atlastile) { + // BIND_CENTER is used to indicate that bitmask should not update for this tile cell. + node->get_tileset()->autotile_set_bitmask(p_value, Vector2(p_pos.x, p_pos.y), TileSet::BIND_CENTER); + node->update_cell_bitmask(p_pos.x, p_pos.y); } } else { - // manually placing tiles should not update bitmasks - if (tool != TOOL_PASTING) { - node->update_bitmask_area(Point2(p_pos)); - } + node->update_bitmask_area(Point2(p_pos)); } } @@ -339,6 +347,11 @@ void TileMapEditor::_manual_toggled(bool p_enabled) { _update_palette(); } +void TileMapEditor::_priority_toggled(bool p_enabled) { + priority_atlastile = p_enabled; + _update_palette(); +} + void TileMapEditor::_text_entered(const String &p_text) { canvas_item_editor_viewport->grab_focus(); @@ -385,6 +398,8 @@ void TileMapEditor::_update_palette() { // Update the palette Vector<int> selected = get_selected_tiles(); + int selected_single = palette->get_current(); + int selected_manual = manual_palette->get_current(); palette->clear(); manual_palette->clear(); manual_palette->hide(); @@ -492,12 +507,13 @@ void TileMapEditor::_update_palette() { if (selected.get(0) != TileMap::INVALID_CELL) { set_selected_tiles(selected); sel_tile = selected.get(Math::rand() % selected.size()); - } else { + } else if (palette->get_item_count() > 0) { palette->select(0); } if (sel_tile != TileMap::INVALID_CELL) { - if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) { + if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || + (!priority_atlastile && tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE)) { const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); @@ -533,16 +549,19 @@ void TileMapEditor::_update_palette() { if (manual_palette->get_item_count() > 0) { // Only show the manual palette if at least tile exists in it - int selected2 = manual_palette->get_current(); - if (selected2 == -1) selected2 = 0; - manual_palette->set_current(selected2); + if (selected_manual == -1 || selected_single != palette->get_current()) + selected_manual = 0; + if (selected_manual < manual_palette->get_item_count()) + manual_palette->set_current(selected_manual); manual_palette->show(); } if (sel_tile != TileMap::INVALID_CELL && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) { manual_button->show(); + priority_button->hide(); } else { manual_button->hide(); + priority_button->show(); } } @@ -553,23 +572,25 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) { if (id == TileMap::INVALID_CELL) return; - if (search_box->get_text().strip_edges() != "") { - + if (search_box->get_text() != "") { search_box->set_text(""); _update_palette(); } - Vector<int> selected; - - selected.push_back(id); - set_selected_tiles(selected); - flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y); flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y); transpose = node->is_cell_transposed(p_pos.x, p_pos.y); autotile_coord = node->get_cell_autotile_coord(p_pos.x, p_pos.y); + Vector<int> selected; + selected.push_back(id); + set_selected_tiles(selected); _update_palette(); + + if ((manual_autotile && node->get_tileset()->tile_get_tile_mode(id) == TileSet::AUTO_TILE) || (!priority_atlastile && node->get_tileset()->tile_get_tile_mode(id) == TileSet::ATLAS_TILE)) { + manual_palette->select(manual_palette->find_metadata((Point2)autotile_coord)); + } + CanvasItemEditor::get_singleton()->update_viewport(); } @@ -686,7 +707,8 @@ void TileMapEditor::_fill_points(const PoolVector<Vector2> p_points, const Dicti _set_cell(pr[i], ids, xf, yf, tr); node->make_bitmask_area_dirty(pr[i]); } - node->update_dirty_bitmask(); + if (!manual_autotile) + node->update_dirty_bitmask(); } void TileMapEditor::_erase_points(const PoolVector<Vector2> p_points) { @@ -744,15 +766,15 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p Rect2 r = node->get_tileset()->tile_get_region(p_cell); if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) { Vector2 offset; - int selected = manual_palette->get_current(); - if ((manual_autotile || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) && selected != -1) { - offset = manual_palette->get_item_metadata(selected); - } else { - if (tool != TOOL_PASTING) { - offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell); + if (tool != TOOL_PASTING) { + int selected = manual_palette->get_current(); + if ((manual_autotile || (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE && !priority_atlastile)) && selected != -1) { + offset = manual_palette->get_item_metadata(selected); } else { - offset = p_autotile_coord; + offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell); } + } else { + offset = p_autotile_coord; } int spacing = node->get_tileset()->autotile_get_spacing(p_cell); @@ -760,7 +782,8 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p r.position += (r.size + Vector2(spacing, spacing)) * offset; } Size2 sc = p_xform.get_scale(); - + /* For a future CheckBox to Center Texture: + Size2 cell_size = node->get_cell_size(); */ Rect2 rect = Rect2(); rect.position = node->map_to_world(p_point) + node->get_cell_draw_offset(); @@ -770,72 +793,37 @@ void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p rect.size = r.size; } - if (rect.size.y > rect.size.x) { - if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose)) - tile_ofs.y += rect.size.y - rect.size.x; - } else if (rect.size.y < rect.size.x) { - if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose)) - tile_ofs.x += rect.size.x - rect.size.y; - } - if (p_transpose) { SWAP(tile_ofs.x, tile_ofs.y); + /* For a future CheckBox to Center Texture: + rect.position.x += cell_size.x / 2 - rect.size.y / 2; + rect.position.y += cell_size.y / 2 - rect.size.x / 2; + } else { + rect.position += cell_size / 2 - rect.size / 2; */ } + if (p_flip_h) { sc.x *= -1.0; tile_ofs.x *= -1.0; } + if (p_flip_v) { sc.y *= -1.0; tile_ofs.y *= -1.0; } - if (node->get_tile_origin() == TileMap::TILE_ORIGIN_TOP_LEFT) { - - rect.position += tile_ofs; - } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_BOTTOM_LEFT) { - Size2 cell_size = node->get_cell_size(); - - rect.position += tile_ofs; - - if (p_transpose) { - if (p_flip_h) - rect.position.x -= cell_size.x; - else - rect.position.x += cell_size.x; - } else { - if (p_flip_v) - rect.position.y -= cell_size.y; - else - rect.position.y += cell_size.y; - } - - } else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) { - Size2 cell_size = node->get_cell_size(); - - rect.position += tile_ofs; - - if (p_flip_h) - rect.position.x -= cell_size.x / 2; - else - rect.position.x += cell_size.x / 2; - - if (p_flip_v) - rect.position.y -= cell_size.y / 2; - else - rect.position.y += cell_size.y / 2; - } - + rect.position += tile_ofs; rect.position = p_xform.xform(rect.position); rect.size *= sc; Color modulate = node->get_tileset()->tile_get_modulate(p_cell); modulate.a = 0.5; - if (r.has_no_area()) + if (r.has_no_area()) { p_viewport->draw_texture_rect(t, rect, false, modulate, p_transpose); - else + } else { p_viewport->draw_texture_rect_region(t, rect, r, modulate, p_transpose); + } } void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Point2i p_autotile_coord, const Transform2D &p_xform) { @@ -870,7 +858,6 @@ void TileMapEditor::_update_copydata() { TileData tcd; tcd.cell = node->get_cell(j, i); - if (tcd.cell != TileMap::INVALID_CELL) { tcd.pos = Point2i(j, i); tcd.flip_h = node->is_cell_x_flipped(j, i); @@ -997,7 +984,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } else { - // Mousebutton was released + // Mousebutton was released. if (tool != TOOL_NONE) { if (tool == TOOL_PAINTING) { @@ -1061,7 +1048,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); - return true; // We want to keep the Pasting tool + return true; // We want to keep the Pasting tool. } else if (tool == TOOL_SELECTING) { CanvasItemEditor::get_singleton()->update_viewport(); @@ -1085,7 +1072,10 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _finish_undo(); - // We want to keep the bucket-tool active + // So the fill preview is cleared right after the click. + CanvasItemEditor::get_singleton()->update_viewport(); + + // We want to keep the bucket-tool active. return true; } @@ -1212,7 +1202,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (tool == TOOL_PAINTING) { - // Paint using bresenham line to prevent holes in painting if the user moves fast + // Paint using bresenham line to prevent holes in painting if the user moves fast. Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y); Vector<int> ids = get_selected_tiles(); @@ -1233,7 +1223,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (tool == TOOL_ERASING) { - // erase using bresenham line to prevent holes in painting if the user moves fast + // Erase using bresenham line to prevent holes in painting if the user moves fast. Vector<Point2i> points = line(old_over_tile.x, over_tile.x, old_over_tile.y, over_tile.y); @@ -1358,13 +1348,13 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } if (!mouse_over) { - // Editor shortcuts should not fire if mouse not in viewport + // Editor shortcuts should not fire if mouse not in viewport. return false; } if (ED_IS_SHORTCUT("tile_map_editor/paint_tile", p_event)) { // NOTE: We do not set tool = TOOL_PAINTING as this begins painting - // immediately without pressing the left mouse button first + // immediately without pressing the left mouse button first. tool = TOOL_NONE; CanvasItemEditor::get_singleton()->update_viewport(); @@ -1446,7 +1436,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); return true; } - } else if (k.is_valid()) { // release event + } else if (k.is_valid()) { // Release event. if (tool == TOOL_NONE) { @@ -1462,7 +1452,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { #else if (k->get_scancode() == KEY_CONTROL) { #endif - // go back to that last tool if KEY_CONTROL was released + // Go back to that last tool if KEY_CONTROL was released. tool = last_tool; CanvasItemEditor::get_singleton()->update_viewport(); @@ -1750,6 +1740,7 @@ void TileMapEditor::_icon_size_changed(float p_value) { void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_manual_toggled"), &TileMapEditor::_manual_toggled); + ClassDB::bind_method(D_METHOD("_priority_toggled"), &TileMapEditor::_priority_toggled); ClassDB::bind_method(D_METHOD("_text_entered"), &TileMapEditor::_text_entered); ClassDB::bind_method(D_METHOD("_text_changed"), &TileMapEditor::_text_changed); ClassDB::bind_method(D_METHOD("_sbox_input"), &TileMapEditor::_sbox_input); @@ -1853,6 +1844,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { node = NULL; manual_autotile = false; + priority_atlastile = false; manual_position = Vector2(0, 0); canvas_item_editor_viewport = NULL; editor = p_editor; @@ -1883,10 +1875,15 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { add_child(tool_hb); manual_button = memnew(CheckBox); - manual_button->set_text("Disable Autotile"); + manual_button->set_text(TTR("Disable Autotile")); manual_button->connect("toggled", this, "_manual_toggled"); add_child(manual_button); + priority_button = memnew(CheckBox); + priority_button->set_text(TTR("Enable Priority")); + priority_button->connect("toggled", this, "_priority_toggled"); + add_child(priority_button); + search_box = memnew(LineEdit); search_box->set_h_size_flags(SIZE_EXPAND_FILL); search_box->connect("text_entered", this, "_text_entered"); @@ -1995,31 +1992,31 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { p->connect("id_pressed", this, "_menu_option"); rotate_left_button = memnew(ToolButton); - rotate_left_button->set_tooltip(TTR("Rotate left")); + rotate_left_button->set_tooltip(TTR("Rotate Left")); rotate_left_button->set_focus_mode(FOCUS_NONE); rotate_left_button->connect("pressed", this, "_rotate", varray(-1)); tool_hb->add_child(rotate_left_button); rotate_right_button = memnew(ToolButton); - rotate_right_button->set_tooltip(TTR("Rotate right")); + rotate_right_button->set_tooltip(TTR("Rotate Right")); rotate_right_button->set_focus_mode(FOCUS_NONE); rotate_right_button->connect("pressed", this, "_rotate", varray(1)); tool_hb->add_child(rotate_right_button); flip_horizontal_button = memnew(ToolButton); - flip_horizontal_button->set_tooltip(TTR("Flip horizontally")); + flip_horizontal_button->set_tooltip(TTR("Flip Horizontally")); flip_horizontal_button->set_focus_mode(FOCUS_NONE); flip_horizontal_button->connect("pressed", this, "_flip_horizontal"); tool_hb->add_child(flip_horizontal_button); flip_vertical_button = memnew(ToolButton); - flip_vertical_button->set_tooltip(TTR("Flip vertically")); + flip_vertical_button->set_tooltip(TTR("Flip Vertically")); flip_vertical_button->set_focus_mode(FOCUS_NONE); flip_vertical_button->connect("pressed", this, "_flip_vertical"); tool_hb->add_child(flip_vertical_button); clear_transform_button = memnew(ToolButton); - clear_transform_button->set_tooltip(TTR("Clear transform")); + clear_transform_button->set_tooltip(TTR("Clear Transform")); clear_transform_button->set_focus_mode(FOCUS_NONE); clear_transform_button->connect("pressed", this, "_clear_transform"); tool_hb->add_child(clear_transform_button); diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index fcdada1111..3f0abd1e6e 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -74,6 +74,7 @@ class TileMapEditor : public VBoxContainer { TileMap *node; bool manual_autotile; + bool priority_atlastile; Vector2 manual_position; EditorNode *editor; @@ -103,6 +104,7 @@ class TileMapEditor : public VBoxContainer { ToolButton *clear_transform_button; CheckBox *manual_button; + CheckBox *priority_button; Tool tool; Tool last_tool; @@ -183,6 +185,7 @@ class TileMapEditor : public VBoxContainer { void set_selected_tiles(Vector<int> p_tile); void _manual_toggled(bool p_enabled); + void _priority_toggled(bool p_enabled); void _text_entered(const String &p_text); void _text_changed(const String &p_text); void _sbox_input(const Ref<InputEvent> &p_ie); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 21470d81ed..4b225fddf5 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -372,6 +372,15 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tool_editmode[EDITMODE_COLLISION]->set_pressed(true); edit_mode = EDITMODE_COLLISION; + tool_editmode[EDITMODE_REGION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_region", TTR("Region Mode"), KEY_1)); + tool_editmode[EDITMODE_COLLISION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_collision", TTR("Collision Mode"), KEY_2)); + tool_editmode[EDITMODE_OCCLUSION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_occlusion", TTR("Occlusion Mode"), KEY_3)); + tool_editmode[EDITMODE_NAVIGATION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_navigation", TTR("Navigation Mode"), KEY_4)); + tool_editmode[EDITMODE_BITMASK]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_bitmask", TTR("Bitmask Mode"), KEY_5)); + tool_editmode[EDITMODE_PRIORITY]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_priority", TTR("Priority Mode"), KEY_6)); + tool_editmode[EDITMODE_ICON]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_icon", TTR("Icon Mode"), KEY_7)); + tool_editmode[EDITMODE_Z_INDEX]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_z_index", TTR("Z Index Mode"), KEY_8)); + main_vb->add_child(tool_hb); separator_editmode = memnew(HSeparator); main_vb->add_child(separator_editmode); @@ -1654,7 +1663,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { edited_collision_shape = _convex; _set_edited_shape_points(_get_collision_shape_points(concave)); } else { - // Shoudn't haphen + // Shouldn't happen } for (int i = 0; i < sd.size(); i++) { if (sd[i].get("shape") == previous_shape) { @@ -1892,7 +1901,7 @@ void TileSetEditor::_update_toggle_shape_button() { tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConcavePolygonShape2D", "EditorIcons")); tools[SHAPE_TOGGLE_TYPE]->set_text("Make Concave"); } else { - // Shoudn't happen + // Shouldn't happen separator_shape_toggle->hide(); tools[SHAPE_TOGGLE_TYPE]->hide(); } @@ -3095,7 +3104,6 @@ void TileSetEditor::update_workspace_tile_mode() { _select_edited_shape_coord(); tool_editmode[EDITMODE_BITMASK]->hide(); - tool_editmode[EDITMODE_PRIORITY]->hide(); } _on_edit_mode_changed(edit_mode); } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index f3eb5d1483..a1b903576e 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "visual_shader_editor_plugin.h" #include "core/io/resource_loader.h" +#include "core/math/math_defs.h" #include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" @@ -68,6 +69,7 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { } } visual_shader = Ref<VisualShader>(p_visual_shader); + visual_shader->set_graph_offset(graph->get_scroll_ofs() / EDSCALE); } else { visual_shader.unref(); } @@ -358,7 +360,9 @@ void VisualShaderEditor::_update_graph() { for (int i = 0; i < graph->get_child_count(); i++) { if (Object::cast_to<GraphNode>(graph->get_child(i))) { - memdelete(graph->get_child(i)); + Node *node = graph->get_child(i); + graph->remove_child(node); + memdelete(node); i--; } } @@ -377,13 +381,33 @@ void VisualShaderEditor::_update_graph() { Vector<int> nodes = visual_shader->get_node_list(type); + Control *offset; + for (int n_i = 0; n_i < nodes.size(); n_i++) { Vector2 position = visual_shader->get_node_position(type, nodes[n_i]); Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]); + Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr()); + bool is_group = !group_node.is_null(); + Size2 size = Size2(0, 0); + + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr()); + bool is_expression = !expression_node.is_null(); + String expression = ""; + GraphNode *node = memnew(GraphNode); + if (is_group) { + size = group_node->get_size(); + + node->set_resizable(true); + node->connect("resize_request", this, "_node_resized", varray((int)type, nodes[n_i])); + } + if (is_expression) { + expression = expression_node->get_expression(); + } + /*if (!vsnode->is_connected("changed", this, "_node_changed")) { vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED); }*/ @@ -403,6 +427,10 @@ void VisualShaderEditor::_update_graph() { Control *custom_editor = NULL; int port_offset = 0; + if (is_group) { + port_offset++; + } + Ref<VisualShaderNodeUniform> uniform = vsnode; if (uniform.is_valid()) { graph->add_child(node); @@ -438,6 +466,24 @@ void VisualShaderEditor::_update_graph() { custom_editor = NULL; } + if (is_group) { + HBoxContainer *hb2 = memnew(HBoxContainer); + + Button *add_input_btn = memnew(Button); + add_input_btn->set_text(TTR("Add input +")); + add_input_btn->connect("pressed", this, "_add_input_port", varray(nodes[n_i], group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "input" + itos(group_node->get_free_input_port_id())), CONNECT_DEFERRED); + hb2->add_child(add_input_btn); + + hb2->add_spacer(); + + Button *add_output_btn = memnew(Button); + add_output_btn->set_text(TTR("Add output +")); + add_output_btn->connect("pressed", this, "_add_output_port", varray(nodes[n_i], group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "output" + itos(group_node->get_free_output_port_id())), CONNECT_DEFERRED); + hb2->add_child(add_output_btn); + + node->add_child(hb2); + } + for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) { if (vsnode->is_port_separator(i)) { @@ -507,21 +553,75 @@ void VisualShaderEditor::_update_graph() { if (valid_left) { - Label *label = memnew(Label); - label->set_text(name_left); - label->add_style_override("normal", label_style); //more compact - hb->add_child(label); + if (is_group) { + + OptionButton *type_box = memnew(OptionButton); + hb->add_child(type_box); + type_box->add_item(TTR("Scalar")); + type_box->add_item(TTR("Vector")); + type_box->add_item(TTR("Boolean")); + type_box->add_item(TTR("Transform")); + type_box->select(group_node->get_input_port_type(i)); + type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + type_box->connect("item_selected", this, "_change_input_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED); + + LineEdit *name_box = memnew(LineEdit); + hb->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(name_left); + name_box->set_expand_to_text_length(true); + name_box->connect("text_entered", this, "_change_input_port_name", varray(name_box, nodes[n_i], i)); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, false)); + + if (is_group) { + Button *remove_btn = memnew(Button); + remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->connect("pressed", this, "_remove_input_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(remove_btn); + } + } else { + + Label *label = memnew(Label); + label->set_text(name_left); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } } hb->add_spacer(); if (valid_right) { - - Label *label = memnew(Label); - label->set_text(name_right); - label->set_align(Label::ALIGN_RIGHT); - label->add_style_override("normal", label_style); //more compact - hb->add_child(label); + if (is_group) { + Button *remove_btn = memnew(Button); + remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->connect("pressed", this, "_remove_output_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(remove_btn); + + LineEdit *name_box = memnew(LineEdit); + hb->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(name_right); + name_box->set_expand_to_text_length(true); + name_box->connect("text_entered", this, "_change_output_port_name", varray(name_box, nodes[n_i], i)); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, true)); + + OptionButton *type_box = memnew(OptionButton); + hb->add_child(type_box); + type_box->add_item(TTR("Scalar")); + type_box->add_item(TTR("Vector")); + type_box->add_item(TTR("Boolean")); + type_box->add_item(TTR("Transform")); + type_box->select(group_node->get_output_port_type(i)); + type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + type_box->connect("item_selected", this, "_change_output_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED); + } else { + Label *label = memnew(Label); + label->set_text(name_right); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } } } @@ -540,18 +640,33 @@ void VisualShaderEditor::_update_graph() { hb->add_child(preview); } + if (is_group) { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + port_offset++; + } + node->add_child(hb); node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]); } if (vsnode->get_output_port_for_preview() >= 0 && vsnode->get_output_port_type(vsnode->get_output_port_for_preview()) != VisualShaderNode::PORT_TYPE_TRANSFORM) { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview()); port_preview->set_h_size_flags(SIZE_SHRINK_CENTER); node->add_child(port_preview); } + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + String error = vsnode->get_warning(visual_shader->get_mode(), type); if (error != String()) { Label *error_label = memnew(Label); @@ -560,9 +675,42 @@ void VisualShaderEditor::_update_graph() { node->add_child(error_label); } + if (is_expression) { + + TextEdit *expression_box = memnew(TextEdit); + expression_node->set_control(expression_box, 0); + node->add_child(expression_box); + + Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); + Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); + Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); + Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); + + expression_box->set_syntax_coloring(true); + + for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) { + + expression_box->add_keyword_color(E->get(), keyword_color); + } + + expression_box->add_font_override("font", get_font("expression", "EditorFonts")); + expression_box->add_color_override("font_color", text_color); + expression_box->add_color_override("symbol_color", symbol_color); + expression_box->add_color_region("/*", "*/", comment_color, false); + expression_box->add_color_region("//", "", comment_color, false); + + expression_box->set_text(expression); + expression_box->set_context_menu_enabled(false); + expression_box->set_show_line_numbers(true); + + expression_box->connect("focus_exited", this, "_expression_focus_out", varray(expression_box, nodes[n_i])); + } + if (!uniform.is_valid()) { graph->add_child(node); _update_created_node(node); + if (is_group) + call_deferred("_set_node_size", (int)type, nodes[n_i], size); } } @@ -577,6 +725,285 @@ void VisualShaderEditor::_update_graph() { } } +void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type, const String &p_name) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Add input port")); + undo_redo->add_do_method(node.ptr(), "add_input_port", p_port, p_port_type, p_name); + undo_redo->add_undo_method(node.ptr(), "remove_input_port", p_port); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_add_output_port(int p_node, int p_port, int p_port_type, const String &p_name) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Add output port")); + undo_redo->add_do_method(node.ptr(), "add_output_port", p_port, p_port_type, p_name); + undo_redo->add_undo_method(node.ptr(), "remove_output_port", p_port); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_input_port_type(int p_type, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Change input port type")); + undo_redo->add_do_method(node.ptr(), "set_input_port_type", p_port, p_type); + undo_redo->add_undo_method(node.ptr(), "set_input_port_type", p_port, node->get_input_port_type(p_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_output_port_type(int p_type, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Change output port type")); + undo_redo->add_do_method(node.ptr(), "set_output_port_type", p_port, p_type); + undo_redo->add_undo_method(node.ptr(), "set_output_port_type", p_port, node->get_output_port_type(p_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + undo_redo->create_action(TTR("Change input port name")); + undo_redo->add_do_method(node.ptr(), "set_input_port_name", p_port_id, p_text); + undo_redo->add_undo_method(node.ptr(), "set_input_port_name", p_port_id, node->get_input_port_name(p_port_id)); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + undo_redo->create_action(TTR("Change output port name")); + undo_redo->add_do_method(node.ptr(), "set_output_port_name", p_port_id, p_text); + undo_redo->add_undo_method(node.ptr(), "set_output_port_name", p_port_id, node->get_output_port_name(p_port_id)); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_remove_input_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Remove input port")); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (to_node == p_node) { + if (to_port == p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + } else if (to_port > p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port - 1); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port - 1); + } + } + } + + undo_redo->add_do_method(node.ptr(), "remove_input_port", p_port); + undo_redo->add_undo_method(node.ptr(), "add_input_port", p_port, (int)node->get_input_port_type(p_port), node->get_input_port_name(p_port)); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + + undo_redo->commit_action(); +} + +void VisualShaderEditor::_remove_output_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Remove output port")); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (from_node == p_node) { + if (from_port == p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + } else if (from_port > p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port - 1, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - 1, to_node, to_port); + } + } + } + + undo_redo->add_do_method(node.ptr(), "remove_output_port", p_port); + undo_redo->add_undo_method(node.ptr(), "add_output_port", p_port, (int)node->get_output_port_type(p_port), node->get_output_port_name(p_port)); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + + undo_redo->commit_action(); +} + +void VisualShaderEditor::_expression_focus_out(Object *text_edit, int p_node) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + TextEdit *expression_box = Object::cast_to<TextEdit>(text_edit); + + if (node->get_expression() == expression_box->get_text()) + return; + + undo_redo->create_action(TTR("Set expression")); + undo_redo->add_do_method(node.ptr(), "set_expression", expression_box->get_text()); + undo_redo->add_undo_method(node.ptr(), "set_expression", node->get_expression()); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_rebuild() { + EditorNode::get_singleton()->get_log()->clear(); + visual_shader->rebuild(); +} + +void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p_size) { + + VisualShader::Type type = VisualShader::Type(p_type); + Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + + if (group_node.is_null()) { + return; + } + + Vector2 size = p_size; + + group_node->set_size(size); + + GraphNode *gn = NULL; + if (edit_type->get_selected() == p_type) { // check - otherwise the error will be emitted + Node *node2 = graph->get_node(itos(p_node)); + gn = Object::cast_to<GraphNode>(node2); + if (!gn) + return; + + gn->set_custom_minimum_size(size); + gn->set_size(Size2(1, 1)); + } + + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (!expression_node.is_null()) { + Control *text_box = expression_node->get_control(0); + Size2 box_size = size; + if (gn != NULL) { + if (box_size.x < 150 * EDSCALE || box_size.y < 0) { + box_size.x = gn->get_size().x; + } + } + box_size.x -= text_box->get_margin(MARGIN_LEFT); + box_size.x -= 28 * EDSCALE; + box_size.y -= text_box->get_margin(MARGIN_TOP); + box_size.y -= 28 * EDSCALE; + text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y)); + text_box->set_size(Size2(1, 1)); + } +} + +void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, int p_node) { + + VisualShader::Type type = VisualShader::Type(p_type); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Resize VisualShader node"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size / EDSCALE); + undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size()); + undo_redo->commit_action(); +} + void VisualShaderEditor::_preview_select_port(int p_node, int p_port) { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -623,6 +1050,52 @@ void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id) _line_edit_changed(text, line_edit, p_node_id); } +void VisualShaderEditor::_port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + String text = Object::cast_to<LineEdit>(line_edit)->get_text(); + + if (!p_output) { + if (node->get_input_port_name(p_port_id) == text) + return; + } else { + if (node->get_output_port_name(p_port_id) == text) + return; + } + + List<String> input_names; + List<String> output_names; + + for (int i = 0; i < node->get_input_port_count(); i++) { + if (!p_output && i == p_port_id) continue; + input_names.push_back(node->get_input_port_name(i)); + } + for (int i = 0; i < node->get_output_port_count(); i++) { + if (p_output && i == p_port_id) continue; + output_names.push_back(node->get_output_port_name(i)); + } + + String validated_name = visual_shader->validate_port_name(text, input_names, output_names); + if (validated_name == "") { + if (!p_output) { + Object::cast_to<LineEdit>(line_edit)->set_text(node->get_input_port_name(p_port_id)); + } else { + Object::cast_to<LineEdit>(line_edit)->set_text(node->get_output_port_name(p_port_id)); + } + return; + } + + if (!p_output) { + _change_input_port_name(validated_name, line_edit, p_node_id, p_port_id); + } else { + _change_output_port_name(validated_name, line_edit, p_node_id, p_port_id); + } +} + void VisualShaderEditor::_port_edited() { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -667,6 +1140,13 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!vsn); + VisualShaderNodeScalarConstant *constant = Object::cast_to<VisualShaderNodeScalarConstant>(vsn); + + if (constant) { + if ((int)add_options[p_idx].value != -1) + constant->set_constant(add_options[p_idx].value); + } + if (p_op_idx != -1) { VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsn); @@ -757,6 +1237,12 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { undo_redo->create_action(TTR("Add Node to Visual Shader")); undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use); + + VisualShaderNodeExpression *expr = Object::cast_to<VisualShaderNodeExpression>(vsnode.ptr()); + if (expr) { + undo_redo->add_do_method(expr, "set_size", Size2(250 * EDSCALE, 150 * EDSCALE)); + } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -831,10 +1317,25 @@ void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_s void VisualShaderEditor::_delete_request(int which) { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNode> node = Ref<VisualShaderNode>(visual_shader->get_node(type, which)); undo_redo->create_action(TTR("Delete Node")); undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which); - undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, which), which); + + // restore size, inputs and outputs if node is group + VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (group) { + undo_redo->add_undo_method(group, "set_size", group->get_size()); + undo_redo->add_undo_method(group, "set_inputs", group->get_inputs()); + undo_redo->add_undo_method(group, "set_outputs", group->get_outputs()); + } + + // restore expression text if node is expression + VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (expression) { + undo_redo->add_undo_method(expression, "set_expression", expression->get_expression()); + } List<VisualShader::Connection> conns; visual_shader->get_node_connections(type, &conns); @@ -1022,6 +1523,19 @@ void VisualShaderEditor::_duplicate_nodes() { undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from); + // duplicate size, inputs and outputs if node is group + Ref<VisualShaderNodeGroupBase> group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (!group.is_null()) { + undo_redo->add_do_method(dupli.ptr(), "set_size", group->get_size()); + undo_redo->add_do_method(dupli.ptr(), "set_inputs", group->get_inputs()); + undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_outputs()); + } + // duplicate expression text if node is expression + Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (!expression.is_null()) { + undo_redo->add_do_method(dupli.ptr(), "set_expression", expression->get_expression()); + } + id_from++; } @@ -1030,7 +1544,7 @@ void VisualShaderEditor::_duplicate_nodes() { for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) { - undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); } } @@ -1073,8 +1587,25 @@ void VisualShaderEditor::_on_nodes_delete() { undo_redo->create_action(TTR("Delete Nodes")); for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + + Ref<VisualShaderNode> node = visual_shader->get_node(type, F->get()); + undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get()); - undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, F->get()), visual_shader->get_node_position(type, F->get()), F->get()); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F->get()), F->get()); + + // restore size, inputs and outputs if node is group + VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (group) { + undo_redo->add_undo_method(group, "set_size", group->get_size()); + undo_redo->add_undo_method(group, "set_inputs", group->get_inputs()); + undo_redo->add_undo_method(group, "set_outputs", group->get_outputs()); + } + + // restore expression text if node is expression + VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (expression) { + undo_redo->add_undo_method(expression, "set_expression", expression->get_expression()); + } } List<VisualShader::Connection> conns; @@ -1267,8 +1798,10 @@ void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } void VisualShaderEditor::_bind_methods() { + ClassDB::bind_method("_rebuild", &VisualShaderEditor::_rebuild); ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); ClassDB::bind_method("_update_options_menu", &VisualShaderEditor::_update_options_menu); + ClassDB::bind_method("_expression_focus_out", &VisualShaderEditor::_expression_focus_out); ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node); ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged); ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request); @@ -1283,11 +1816,22 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty); ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out); ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed); + ClassDB::bind_method("_port_name_focus_out", &VisualShaderEditor::_port_name_focus_out); ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes); ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected); ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port); ClassDB::bind_method("_graph_gui_input", &VisualShaderEditor::_graph_gui_input); + ClassDB::bind_method("_add_input_port", &VisualShaderEditor::_add_input_port); + ClassDB::bind_method("_change_input_port_type", &VisualShaderEditor::_change_input_port_type); + ClassDB::bind_method("_change_input_port_name", &VisualShaderEditor::_change_input_port_name); + ClassDB::bind_method("_remove_input_port", &VisualShaderEditor::_remove_input_port); + ClassDB::bind_method("_add_output_port", &VisualShaderEditor::_add_output_port); + ClassDB::bind_method("_change_output_port_type", &VisualShaderEditor::_change_output_port_type); + ClassDB::bind_method("_change_output_port_name", &VisualShaderEditor::_change_output_port_name); + ClassDB::bind_method("_remove_output_port", &VisualShaderEditor::_remove_output_port); + ClassDB::bind_method("_node_resized", &VisualShaderEditor::_node_resized); + ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size); ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); @@ -1311,6 +1855,7 @@ VisualShaderEditor::VisualShaderEditor() { updating = false; saved_node_pos_dirty = false; saved_node_pos = Point2(0, 0); + ShaderLanguage::get_keyword_list(&keyword_list); graph = memnew(GraphEdit); add_child(graph); @@ -1451,6 +1996,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); // BOOLEAN + add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated vector if the provided boolean value is true or false."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); @@ -1564,6 +2110,19 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("ScalarFunc", "Scalar", "Common", "VisualShaderNodeScalarFunc", TTR("Scalar function."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("ScalarOp", "Scalar", "Common", "VisualShaderNodeScalarOp", TTR("Scalar operator."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + //CONSTANTS + + add_options.push_back(AddOption("E", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("E constant (2.718282). Represents the base of the natural logarithm."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_E)); + add_options.push_back(AddOption("Epsilon", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Epsilon constant (0.00001). Smallest possible scalar number."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, CMP_EPSILON)); + add_options.push_back(AddOption("Phi", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Phi constant (1.618034). Golden ratio."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, 1.618034f)); + add_options.push_back(AddOption("Pi/4", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi/4 constant (0.785398) or 45 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI / 4)); + add_options.push_back(AddOption("Pi/2", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi/2 constant (1.570796) or 90 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI / 2)); + add_options.push_back(AddOption("Pi", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi constant (3.141593) or 180 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI)); + add_options.push_back(AddOption("Tau", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Tau constant (6.283185) or 360 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_TAU)); + add_options.push_back(AddOption("Sqrt2", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Sqrt2 constant (1.414214). Square root of 2."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_SQRT2)); + + // FUNCTIONS + add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeScalarFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("ACos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("ACosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_SCALAR)); @@ -1588,6 +2147,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the lesser of two values."), VisualShaderNodeScalarOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeScalarInterp", TTR("Linear interpolation between two scalars."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeScalarFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("OneMinus", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 - scalar"), VisualShaderNodeScalarFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeScalarOp::OP_POW, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeScalarFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 / scalar"), VisualShaderNodeScalarFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_SCALAR)); @@ -1677,6 +2237,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeVectorInterp", TTR("Linear interpolation between two vectors."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeVectorOp::OP_POW, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_VECTOR)); @@ -1708,6 +2269,9 @@ VisualShaderEditor::VisualShaderEditor() { // SPECIAL + add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside."))); + add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index eb0dee7594..1b009b61d5 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -101,8 +101,9 @@ class VisualShaderEditor : public VBoxContainer { int mode; int return_type; int func; + float value; - AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), int p_sub_func = -1, int p_return_type = -1, int p_mode = -1, int p_func = -1) { + AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), int p_sub_func = -1, int p_return_type = -1, int p_mode = -1, int p_func = -1, float p_value = -1) { name = p_name; type = p_type; category = p_category; @@ -112,9 +113,10 @@ class VisualShaderEditor : public VBoxContainer { return_type = p_return_type; mode = p_mode; func = p_func; + value = p_value; } - AddOption(const String &p_name, const String &p_category, const String &p_sub_category, const String &p_type, const String &p_description, const String &p_sub_func, int p_return_type = -1, int p_mode = -1, int p_func = -1) { + AddOption(const String &p_name, const String &p_category, const String &p_sub_category, const String &p_type, const String &p_description, const String &p_sub_func, int p_return_type = -1, int p_mode = -1, int p_func = -1, float p_value = -1) { name = p_name; type = p_type; category = p_category; @@ -124,10 +126,12 @@ class VisualShaderEditor : public VBoxContainer { return_type = p_return_type; mode = p_mode; func = p_func; + value = p_value; } }; Vector<AddOption> add_options; + List<String> keyword_list; void _draw_color_over_button(Object *obj, Color p_color); @@ -160,14 +164,32 @@ class VisualShaderEditor : public VBoxContainer { void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id); void _line_edit_focus_out(Object *line_edit, int p_node_id); + void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output); + void _duplicate_nodes(); Vector<Ref<VisualShaderNodePlugin> > plugins; void _mode_selected(int p_id); + void _rebuild(); void _input_select_item(Ref<VisualShaderNodeInput> input, String name); + void _add_input_port(int p_node, int p_port, int p_type, const String &p_name); + void _remove_input_port(int p_node, int p_port); + void _change_input_port_type(int p_type, int p_node, int p_port); + void _change_input_port_name(const String &p_text, Object *line_edit, int p_node, int p_port); + + void _add_output_port(int p_node, int p_port, int p_type, const String &p_name); + void _remove_output_port(int p_node, int p_port); + void _change_output_port_type(int p_type, int p_node, int p_port); + void _change_output_port_name(const String &p_text, Object *line_edit, int p_node, int p_port); + + void _expression_focus_out(Object *text_edit, int p_node); + + void _set_node_size(int p_type, int p_node, const Size2 &p_size); + void _node_resized(const Vector2 &p_new_size, int p_type, int p_node); + void _preview_select_port(int p_node, int p_port); void _graph_gui_input(const Ref<InputEvent> p_event); |