summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp33
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp10
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp52
-rw-r--r--editor/plugins/asset_library_editor_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp4075
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h157
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.cpp103
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.h25
-rw-r--r--editor/plugins/editor_preview_plugins.cpp68
-rw-r--r--editor/plugins/editor_preview_plugins.h2
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp30
-rw-r--r--editor/plugins/item_list_editor_plugin.h4
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.cpp6
-rw-r--r--editor/plugins/navigation_mesh_generator.cpp34
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/particles_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_editor_plugin.cpp11
-rw-r--r--editor/plugins/physical_bone_plugin.cpp123
-rw-r--r--editor/plugins/physical_bone_plugin.h78
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp748
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.h46
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp29
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h5
-rw-r--r--editor/plugins/script_editor_plugin.cpp355
-rw-r--r--editor/plugins/script_editor_plugin.h33
-rw-r--r--editor/plugins/script_text_editor.cpp153
-rw-r--r--editor/plugins/script_text_editor.h9
-rw-r--r--editor/plugins/shader_graph_editor_plugin.cpp10
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp120
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h55
-rw-r--r--editor/plugins/skeleton_editor_plugin.cpp189
-rw-r--r--editor/plugins/skeleton_editor_plugin.h95
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp219
-rw-r--r--editor/plugins/spatial_editor_plugin.h6
-rw-r--r--editor/plugins/sprite_editor_plugin.cpp402
-rw-r--r--editor/plugins/sprite_editor_plugin.h73
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp30
-rw-r--r--editor/plugins/texture_editor_plugin.cpp3
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp54
-rw-r--r--editor/plugins/texture_region_editor_plugin.h7
-rw-r--r--editor/plugins/theme_editor_plugin.cpp66
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp124
-rw-r--r--editor/plugins/tile_map_editor_plugin.h22
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp1043
-rw-r--r--editor/plugins/tile_set_editor_plugin.h125
45 files changed, 5765 insertions, 3075 deletions
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index c22e1cd88b..b387972558 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -440,7 +440,7 @@ void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource)
file->set_current_path(existing);
}
file->popup_centered_ratio();
- file->set_title(TTR("Save Resource As.."));
+ file->set_title(TTR("Save Resource As..."));
current_option = RESOURCE_SAVE;
}
@@ -459,6 +459,12 @@ void AnimationPlayerEditor::_animation_remove_confirmed() {
Ref<Animation> anim = player->get_animation(current);
undo_redo->create_action(TTR("Remove Animation"));
+ if (player->get_autoplay() == current) {
+ undo_redo->add_do_method(player, "set_autoplay", "");
+ undo_redo->add_undo_method(player, "set_autoplay", current);
+ // Avoid having the autoplay icon linger around if there is only one animation in the player
+ undo_redo->add_do_method(this, "_animation_player_changed", player);
+ }
undo_redo->add_do_method(player, "remove_animation", current);
undo_redo->add_undo_method(player, "add_animation", current, anim);
undo_redo->add_do_method(this, "_animation_player_changed", player);
@@ -491,7 +497,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
String new_name = name->get_text();
if (new_name == "" || new_name.find(":") != -1 || new_name.find("/") != -1) {
- error_dialog->set_text(TTR("ERROR: Invalid animation name!"));
+ error_dialog->set_text(TTR("Invalid animation name!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -502,7 +508,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
}
if (player->has_animation(new_name)) {
- error_dialog->set_text(TTR("ERROR: Animation name already exists!"));
+ error_dialog->set_text(TTR("Animation name already exists!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -811,6 +817,8 @@ void AnimationPlayerEditor::_update_player() {
play_bw->set_disabled(animlist.size() == 0);
play_bw_from->set_disabled(animlist.size() == 0);
play_from->set_disabled(animlist.size() == 0);
+ frame->set_editable(animlist.size() != 0);
+ animation->set_disabled(animlist.size() == 0);
autoplay->set_disabled(animlist.size() == 0);
duplicate_anim->set_disabled(animlist.size() == 0);
rename_anim->set_disabled(animlist.size() == 0);
@@ -820,6 +828,7 @@ void AnimationPlayerEditor::_update_player() {
save_anim->set_disabled(animlist.size() == 0);
tool_anim->set_disabled(player == NULL);
onion_skinning->set_disabled(player == NULL);
+ pin->set_disabled(player == NULL);
int active_idx = -1;
for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) {
@@ -1011,6 +1020,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) {
player->seek_delta(pos, pos - cpos);
} else {
+ player->stop(true);
player->seek(pos, true);
}
@@ -1087,7 +1097,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
case TOOL_COPY_ANIM: {
if (!animation->get_item_count()) {
- error_dialog->set_text(TTR("ERROR: No animation to copy!"));
+ error_dialog->set_text(TTR("No animation to copy!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -1102,7 +1112,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard();
if (!anim.is_valid()) {
- error_dialog->set_text(TTR("ERROR: No animation resource on clipboard!"));
+ error_dialog->set_text(TTR("No animation resource on clipboard!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -1133,7 +1143,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
case TOOL_EDIT_RESOURCE: {
if (!animation->get_item_count()) {
- error_dialog->set_text(TTR("ERROR: No animation to edit!"));
+ error_dialog->set_text(TTR("No animation to edit!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -1610,7 +1620,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
hb->add_child(load_anim);
save_anim = memnew(MenuButton);
- save_anim->set_tooltip(TTR("Save the current animation"));
+ save_anim->set_tooltip(TTR("Save the current animation."));
save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save", TTR("Save")), ANIM_SAVE);
save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as", TTR("Save As")), ANIM_SAVE_AS);
save_anim->set_focus_mode(Control::FOCUS_NONE);
@@ -1678,10 +1688,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
onion_skinning->get_popup()->add_separator();
onion_skinning->get_popup()->add_item(TTR("Depth"), -1);
onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true);
- onion_skinning->get_popup()->add_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
- onion_skinning->get_popup()->add_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
- onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
onion_skinning->get_popup()->add_separator();
onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY);
onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE);
@@ -1690,6 +1700,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
pin = memnew(ToolButton);
pin->set_toggle_mode(true);
+ pin->set_tooltip(TTR("Pin AnimationPlayer"));
hb->add_child(pin);
resource_edit_anim = memnew(Button);
@@ -1717,7 +1728,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
error_dialog = memnew(ConfirmationDialog);
error_dialog->get_ok()->set_text(TTR("Close"));
- error_dialog->set_text(TTR("Error!"));
+ error_dialog->set_title(TTR("Error!"));
add_child(error_dialog);
name_dialog->connect("confirmed", this, "_animation_name_edited");
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index f0e186e4b0..25582ae0b9 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -578,7 +578,7 @@ void AnimationTreeEditor::_draw_node(const StringName &p_node) {
if (anim_tree->animation_node_get_master_animation(p_node) != "")
text = anim_tree->animation_node_get_master_animation(p_node);
else if (anim.is_null())
- text = "load..";
+ text = "load...";
else
text = anim->get_name();
@@ -593,7 +593,7 @@ void AnimationTreeEditor::_draw_node(const StringName &p_node) {
case AnimationTreePlayer::NODE_TIMESCALE:
case AnimationTreePlayer::NODE_TRANSITION: {
- font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit..", font_color_title);
+ font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title);
} break;
default: editable = false;
}
@@ -756,6 +756,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) {
if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) {
node_popup->clear();
+ node_popup->set_size(Size2(1, 1));
node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT);
if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) {
node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT);
@@ -774,6 +775,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) {
if (rclick_type == CLICK_NODE) {
node_popup->clear();
+ node_popup->set_size(Size2(1, 1));
node_popup->add_item(TTR("Rename"), NODE_RENAME);
node_popup->add_item(TTR("Remove"), NODE_ERASE);
if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION)
@@ -1288,7 +1290,7 @@ AnimationTreeEditor::AnimationTreeEditor() {
p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK);
p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION);
p->add_separator();
- p->add_item(TTR("Import Animations.."), MENU_IMPORT_ANIMATIONS); // wtf
+ p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf
p->add_separator();
p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR);
@@ -1397,7 +1399,7 @@ AnimationTreeEditor::AnimationTreeEditor() {
filter_button->set_margin(MARGIN_RIGHT, -10);
edit_dialog->add_child(filter_button);
filter_button->hide();
- filter_button->set_text(TTR("Filters.."));
+ filter_button->set_text(TTR("Filters..."));
filter_button->connect("pressed", this, "_edit_filters");
set_clip_contents(true);
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 915132c75c..05833704d1 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -30,11 +30,10 @@
#include "asset_library_editor_plugin.h"
+#include "core/io/json.h"
+#include "core/version.h"
#include "editor_node.h"
#include "editor_settings.h"
-#include "io/json.h"
-
-#include "version_generated.gen.h"
void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, int p_rating, const String &p_cost) {
@@ -308,7 +307,7 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() {
preview_hb->set_v_size_flags(SIZE_EXPAND_FILL);
previews->add_child(preview_hb);
- get_ok()->set_text(TTR("Install"));
+ get_ok()->set_text(TTR("Download"));
get_cancel()->set_text(TTR("Close"));
}
///////////////////////////////////////////////////////////////////////////////////
@@ -316,7 +315,6 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() {
void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data) {
String error_text;
- print_line("COMPLETED: " + itos(p_status) + " code: " + itos(p_code) + " data size: " + itos(p_data.size()));
switch (p_status) {
@@ -371,7 +369,6 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
progress->set_max(download->get_body_size());
progress->set_value(download->get_downloaded_bytes());
- print_line("max: " + itos(download->get_body_size()) + " bytes: " + itos(download->get_downloaded_bytes()));
install->set_disabled(false);
progress->set_value(download->get_downloaded_bytes());
@@ -410,13 +407,13 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) {
switch (cstatus) {
case HTTPClient::STATUS_RESOLVING: {
- status->set_text(TTR("Resolving.."));
+ status->set_text(TTR("Resolving..."));
} break;
case HTTPClient::STATUS_CONNECTING: {
- status->set_text(TTR("Connecting.."));
+ status->set_text(TTR("Connecting..."));
} break;
case HTTPClient::STATUS_REQUESTING: {
- status->set_text(TTR("Requesting.."));
+ status->set_text(TTR("Requesting..."));
} break;
default: {}
}
@@ -517,6 +514,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() {
download = memnew(HTTPRequest);
add_child(download);
download->connect("request_completed", this, "_http_download_completed");
+ download->set_use_threads(EDITOR_DEF("asset_library/use_threads", true));
download_error = memnew(AcceptDialog);
add_child(download_error);
@@ -536,11 +534,9 @@ void EditorAssetLibrary::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(get_icon("Error", "EditorIcons"));
+ error_tr->set_texture(get_icon("Error", "EditorIcons"));
reverse->set_icon(get_icon("Sort", "EditorIcons"));
- error_hb->add_child(tf);
error_label->raise();
} break;
@@ -588,6 +584,8 @@ void EditorAssetLibrary::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree"));
+ error_tr->set_texture(get_icon("Error", "EditorIcons"));
+ reverse->set_icon(get_icon("Sort", "EditorIcons"));
} break;
}
}
@@ -641,7 +639,7 @@ const char *EditorAssetLibrary::support_key[SUPPORT_MAX] = {
void EditorAssetLibrary::_select_author(int p_id) {
- //opemn author window
+ // Open author window
}
void EditorAssetLibrary::_select_category(int p_id) {
@@ -661,16 +659,6 @@ void EditorAssetLibrary::_select_category(int p_id) {
void EditorAssetLibrary::_select_asset(int p_id) {
_api_request("asset/" + itos(p_id), REQUESTING_ASSET);
-
- /*
- if (description) {
- memdelete(description);
- }
-
-
- description = memnew( EditorAssetLibraryItemDescription );
- add_child(description);
- description->popup_centered_minsize();*/
}
void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByteArray &p_data, int p_queue_id) {
@@ -747,8 +735,6 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
if (p_status == HTTPRequest::RESULT_SUCCESS) {
- print_line("GOT IMAGE YAY!");
-
if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) {
for (int i = 0; i < headers.size(); i++) {
if (headers[i].findn("ETag:") == 0) { // Save etag
@@ -778,7 +764,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
_image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id);
} else {
- WARN_PRINTS("Error getting PNG file for asset id " + itos(image_queue[p_queue_id].asset_id));
+ WARN_PRINTS("Error getting PNG file from URL: " + image_queue[p_queue_id].image_url);
Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target);
if (obj) {
obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons"));
@@ -811,7 +797,6 @@ void EditorAssetLibrary::_update_image_queue() {
}
}
- print_line("REQUEST ICON FOR: " + itos(E->get().asset_id));
Error err = E->get().request->request(E->get().image_url, headers);
if (err != OK) {
to_delete.push_back(E->key());
@@ -838,6 +823,7 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag
iq.image_index = p_image_index;
iq.image_type = p_type;
iq.request = memnew(HTTPRequest);
+ iq.request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true));
iq.target = p_for;
iq.queue_id = ++last_queue_id;
@@ -855,7 +841,6 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag
void EditorAssetLibrary::_repository_changed(int p_repository_id) {
host = repository->get_item_metadata(p_repository_id);
- print_line(".." + host);
if (templates_only) {
_api_request("configure", REQUESTING_CONFIG, "?type=project");
} else {
@@ -883,7 +868,8 @@ void EditorAssetLibrary::_search(int p_page) {
}
args += String() + "sort=" + sort_key[sort->get_selected()];
- args += "&godot_version=" + itos(VERSION_MAJOR) + "." + itos(VERSION_MINOR);
+ // We use the "branch" version, i.e. major.minor, as patch releases should be compatible
+ args += "&godot_version=" + String(VERSION_BRANCH);
String support_list;
for (int i = 0; i < SUPPORT_MAX; i++) {
@@ -1066,8 +1052,6 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
return;
}
- print_line("response: " + itos(p_status) + " code: " + itos(p_code));
-
Dictionary d;
{
Variant js;
@@ -1077,8 +1061,6 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
d = js;
}
- print_line(Variant(d).get_construct_string());
-
RequestType requested = requesting;
requesting = REQUESTING_NONE;
@@ -1390,7 +1372,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
support = memnew(MenuButton);
search_hb2->add_child(support);
- support->set_text(TTR("Support.."));
+ support->set_text(TTR("Support..."));
support->get_popup()->add_check_item(TTR("Official"), SUPPORT_OFFICIAL);
support->get_popup()->add_check_item(TTR("Community"), SUPPORT_COMMUNITY);
support->get_popup()->add_check_item(TTR("Testing"), SUPPORT_TESTING);
@@ -1462,6 +1444,8 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
error_label = memnew(Label);
error_label->add_color_override("color", get_color("error_color", "Editor"));
error_hb->add_child(error_label);
+ error_tr = memnew(TextureRect);
+ error_hb->add_child(error_tr);
description = NULL;
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index b344716c1d..69a65dd3dc 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -194,6 +194,7 @@ class EditorAssetLibrary : public PanelContainer {
Button *search;
ProgressBar *load_status;
HBoxContainer *error_hb;
+ TextureRect *error_tr;
Label *error_label;
MenuButton *support;
@@ -240,7 +241,6 @@ class EditorAssetLibrary : public PanelContainer {
bool active;
int queue_id;
- int asset_id;
ImageType image_type;
int image_index;
String image_url;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 670f13b6c8..93aeca6632 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -44,6 +44,7 @@
#include "scene/2d/particles_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/screen_button.h"
+#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/nine_patch_rect.h"
@@ -169,64 +170,10 @@ public:
}
};
-void CanvasItemEditor::_edit_set_pivot(const Vector2 &mouse_pos) {
- List<Node *> &selection = editor_selection->get_selected_node_list();
-
- undo_redo->create_action(TTR("Move Pivot"));
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- Node2D *n2d = Object::cast_to<Node2D>(E->get());
- if (n2d && n2d->_edit_use_pivot()) {
-
- Vector2 offset = n2d->_edit_get_pivot();
- Vector2 gpos = n2d->get_global_position();
-
- Vector2 local_mouse_pos = n2d->get_canvas_transform().affine_inverse().xform(mouse_pos);
-
- Vector2 motion_ofs = gpos - local_mouse_pos;
-
- undo_redo->add_do_method(n2d, "set_global_position", local_mouse_pos);
- undo_redo->add_do_method(n2d, "_edit_set_pivot", offset + n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs));
- undo_redo->add_undo_method(n2d, "set_global_position", gpos);
- undo_redo->add_undo_method(n2d, "_edit_set_pivot", offset);
- for (int i = 0; i < n2d->get_child_count(); i++) {
- Node2D *n2dc = Object::cast_to<Node2D>(n2d->get_child(i));
- if (!n2dc)
- continue;
-
- undo_redo->add_do_method(n2dc, "set_global_position", n2dc->get_global_position());
- undo_redo->add_undo_method(n2dc, "set_global_position", n2dc->get_global_position());
- }
- }
-
- Control *cnt = Object::cast_to<Control>(E->get());
- if (cnt) {
-
- Vector2 old_pivot = cnt->get_pivot_offset();
- Vector2 new_pivot = cnt->get_global_transform_with_canvas().affine_inverse().xform(mouse_pos);
- Vector2 old_pos = cnt->get_position();
-
- Vector2 top_pos = cnt->get_transform().get_origin(); //remember where top pos was
- cnt->set_pivot_offset(new_pivot);
- Vector2 new_top_pos = cnt->get_transform().get_origin(); //check where it is now
-
- Vector2 new_pos = old_pos - (new_top_pos - top_pos); //offset it back
-
- undo_redo->add_do_method(cnt, "set_pivot_offset", new_pivot);
- undo_redo->add_do_method(cnt, "set_position", new_pos);
- undo_redo->add_undo_method(cnt, "set_pivot_offset", old_pivot);
- undo_redo->add_undo_method(cnt, "set_position", old_pos);
- }
- }
-
- undo_redo->commit_action();
-}
-
void CanvasItemEditor::_snap_if_closer_float(float p_value, float p_target_snap, float &r_current_snap, bool &r_snapped, float p_radius) {
float radius = p_radius / zoom;
float dist = Math::abs(p_value - p_target_snap);
- if (p_radius < 0 || dist < radius && (!r_snapped || dist < Math::abs(r_current_snap - p_value))) {
+ if ((p_radius < 0 || dist < radius) && (!r_snapped || dist < Math::abs(r_current_snap - p_value))) {
r_current_snap = p_target_snap;
r_snapped = true;
}
@@ -250,11 +197,15 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap,
Transform2D ci_transform = canvas_item->get_global_transform_with_canvas();
Transform2D to_snap_transform = p_to_snap ? p_to_snap->get_global_transform_with_canvas() : Transform2D();
if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) {
- Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
- Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
-
- _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
- _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
+ if (canvas_item->_edit_use_rect()) {
+ Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
+ Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
+ _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
+ } else {
+ Point2 position = ci_transform.xform(Point2());
+ _snap_if_closer_point(p_value, position, r_current_snap, r_snapped, ci_transform.get_rotation());
+ }
}
}
for (int i = 0; i < p_current->get_child_count(); i++) {
@@ -263,63 +214,76 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap,
}
Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const CanvasItem *p_canvas_item, unsigned int p_forced_modes) {
- Point2 dist[2];
bool snapped[2] = { false, false };
+ bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL);
// Smart snap using the canvas position
Vector2 output = p_target;
real_t rotation = 0.0;
if (p_canvas_item) {
- Point2 begin;
- Point2 end;
rotation = p_canvas_item->get_global_transform_with_canvas().get_rotation();
- if ((snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) {
- // Parent sides and center
- bool can_snap = false;
+ // Parent sides and center
+ if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) {
if (const Control *c = Object::cast_to<Control>(p_canvas_item)) {
- begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
- end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
- can_snap = true;
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
+ _snap_if_closer_point(p_target, begin, output, snapped, rotation);
+ _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
+ _snap_if_closer_point(p_target, end, output, snapped, rotation);
} else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) {
- begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
- end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
- can_snap = true;
+ if (parent_ci->_edit_use_rect()) {
+ Point2 begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
+ Point2 end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_target, begin, output, snapped, rotation);
+ _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
+ _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ } else {
+ Point2 position = p_canvas_item->get_transform().affine_inverse().xform(Point2());
+ _snap_if_closer_point(p_target, position, output, snapped, rotation);
+ }
}
+ }
- if (can_snap) {
+ // Self anchors
+ if ((is_snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) {
+ if (const Control *c = Object::cast_to<Control>(p_canvas_item)) {
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP))));
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM))));
_snap_if_closer_point(p_target, begin, output, snapped, rotation);
- _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation);
}
}
- // Self anchors (for sides)
- if ((snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) {
- if (const Control *c = Object::cast_to<Control>(p_canvas_item)) {
- begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP))));
- end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM))));
+ // Self sides
+ if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) {
+ if (p_canvas_item->_edit_use_rect()) {
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
_snap_if_closer_point(p_target, begin, output, snapped, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation);
}
}
- // Self sides (for anchors)
- if ((snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) {
- begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
- end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
- _snap_if_closer_point(p_target, begin, output, snapped, rotation);
- _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ // Self center
+ if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) {
+ if (p_canvas_item->_edit_use_rect()) {
+ Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0);
+ _snap_if_closer_point(p_target, center, output, snapped, rotation);
+ } else {
+ Point2 position = p_canvas_item->get_global_transform_with_canvas().xform(Point2());
+ _snap_if_closer_point(p_target, position, output, snapped, rotation);
+ }
}
}
// Other nodes sides
- if ((snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) {
+ if ((is_snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) {
_snap_other_nodes(p_target, output, snapped, get_tree()->get_edited_scene_root(), p_canvas_item);
}
- if (((snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) {
+ if (((is_snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) {
// Guides
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) {
Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_");
@@ -336,15 +300,15 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
}
}
- if (((snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) {
+ if (((is_snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) {
// Grid
Point2 offset = grid_offset;
if (snap_relative) {
- List<Node *> selection = editor_selection->get_selected_node_list();
+ List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1 && Object::cast_to<Node2D>(selection[0])) {
offset = Object::cast_to<Node2D>(selection[0])->get_global_position();
- } else {
- offset = _find_topleftmost_point();
+ } else if (selection.size() > 0) {
+ offset = _get_encompassing_rect_from_list(selection).position;
}
}
Point2 grid_output;
@@ -362,8 +326,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
}
float CanvasItemEditor::snap_angle(float p_target, float p_start) const {
- float offset = snap_relative ? p_start : p_target;
- return (snap_rotation && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target;
+ return (((snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target;
}
void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
@@ -377,48 +340,20 @@ void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
return;
if (k->is_pressed() && !k->is_echo()) {
- if (drag_pivot_shortcut.is_valid() && drag_pivot_shortcut->is_shortcut(p_ev) && drag == DRAG_NONE && can_move_pivot) {
- //move drag pivot
- drag = DRAG_PIVOT;
- } else if (set_pivot_shortcut.is_valid() && set_pivot_shortcut->is_shortcut(p_ev) && drag == DRAG_NONE && can_move_pivot) {
- if (!Input::get_singleton()->is_mouse_button_pressed(0)) {
- List<Node *> selection = editor_selection->get_selected_node_list();
- Vector2 mouse_pos = viewport->get_local_mouse_position();
- if (selection.size() && viewport->get_rect().has_point(mouse_pos)) {
- //just in case, make it work if over viewport
- mouse_pos = transform.affine_inverse().xform(mouse_pos);
- mouse_pos = snap_point(mouse_pos, SNAP_DEFAULT, _get_single_item());
-
- _edit_set_pivot(mouse_pos);
- }
- }
- } else if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) {
+ if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) {
// Multiply the grid size
grid_step_multiplier = MIN(grid_step_multiplier + 1, 12);
- viewport_base->update();
viewport->update();
} else if ((snap_grid || show_grid) && divide_grid_step_shortcut.is_valid() && divide_grid_step_shortcut->is_shortcut(p_ev)) {
// Divide the grid size
Point2 new_grid_step = grid_step * Math::pow(2.0, grid_step_multiplier - 1);
if (new_grid_step.x >= 1.0 && new_grid_step.y >= 1.0)
grid_step_multiplier--;
- viewport_base->update();
viewport->update();
}
}
}
-void CanvasItemEditor::_tool_select(int p_index) {
-
- ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, rotate_button, pivot_button, pan_button };
- for (int i = 0; i < TOOL_MAX; i++) {
- tb[i]->set_pressed(i == p_index);
- }
-
- viewport->update();
- tool = (Tool)p_index;
-}
-
Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
CanvasItem *ci = Object::cast_to<CanvasItem>(p_what);
@@ -428,220 +363,129 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
return memnew(CanvasItemEditorSelectedItem);
}
-Dictionary CanvasItemEditor::get_state() const {
+void CanvasItemEditor::_keying_changed() {
- Dictionary state;
- state["zoom"] = zoom;
- state["ofs"] = Point2(h_scroll->get_value(), v_scroll->get_value());
- //state["ofs"]=-transform.get_origin();
- state["grid_offset"] = grid_offset;
- state["grid_step"] = grid_step;
- state["snap_rotation_offset"] = snap_rotation_offset;
- state["snap_rotation_step"] = snap_rotation_step;
- state["snap_active"] = snap_active;
- state["snap_node_parent"] = snap_node_parent;
- state["snap_node_anchors"] = snap_node_anchors;
- state["snap_node_sides"] = snap_node_sides;
- state["snap_other_nodes"] = snap_other_nodes;
- state["snap_grid"] = snap_grid;
- state["snap_guides"] = snap_guides;
- state["show_grid"] = show_grid;
- state["show_rulers"] = show_rulers;
- state["show_guides"] = show_guides;
- state["show_helpers"] = show_helpers;
- state["snap_rotation"] = snap_rotation;
- state["snap_relative"] = snap_relative;
- state["snap_pixel"] = snap_pixel;
- state["skeleton_show_bones"] = skeleton_show_bones;
- return state;
+ if (AnimationPlayerEditor::singleton->get_key_editor()->is_visible_in_tree())
+ animation_hb->show();
+ else
+ animation_hb->hide();
}
-void CanvasItemEditor::set_state(const Dictionary &p_state) {
-
- Dictionary state = p_state;
- if (state.has("zoom")) {
- zoom = p_state["zoom"];
- }
-
- if (state.has("ofs")) {
- _update_scrollbars(); // i wonder how safe is calling this here..
- Point2 ofs = p_state["ofs"];
- h_scroll->set_value(ofs.x);
- v_scroll->set_value(ofs.y);
- }
- if (state.has("grid_offset")) {
- grid_offset = state["grid_offset"];
- }
-
- if (state.has("grid_step")) {
- grid_step = state["grid_step"];
- }
-
- if (state.has("snap_rotation_step")) {
- snap_rotation_step = state["snap_rotation_step"];
- }
-
- if (state.has("snap_rotation_offset")) {
- snap_rotation_offset = state["snap_rotation_offset"];
- }
-
- if (state.has("snap_active")) {
- snap_active = state["snap_active"];
- snap_button->set_pressed(snap_active);
- }
-
- if (state.has("snap_node_parent")) {
- snap_node_parent = state["snap_node_parent"];
- int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT);
- smartsnap_config_popup->set_item_checked(idx, snap_node_parent);
- }
-
- if (state.has("snap_node_anchors")) {
- snap_node_anchors = state["snap_node_anchors"];
- int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_ANCHORS);
- smartsnap_config_popup->set_item_checked(idx, snap_node_anchors);
- }
-
- if (state.has("snap_node_sides")) {
- snap_node_sides = state["snap_node_sides"];
- int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES);
- smartsnap_config_popup->set_item_checked(idx, snap_node_sides);
- }
-
- if (state.has("snap_other_nodes")) {
- snap_other_nodes = state["snap_other_nodes"];
- int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES);
- smartsnap_config_popup->set_item_checked(idx, snap_other_nodes);
- }
+Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_list) {
+ ERR_FAIL_COND_V(p_list.empty(), Rect2());
- if (state.has("snap_guides")) {
- snap_guides = state["snap_guides"];
- int idx = smartsnap_config_popup->get_item_index(SNAP_USE_GUIDES);
- smartsnap_config_popup->set_item_checked(idx, snap_guides);
+ // Handles the first element
+ CanvasItem *canvas_item = p_list.front()->get();
+ Rect2 rect;
+ if (canvas_item->_edit_use_rect()) {
+ rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ } else {
+ rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(Point2()), Size2());
}
- if (state.has("snap_grid")) {
- snap_grid = state["snap_grid"];
- int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID);
- snap_config_menu->get_popup()->set_item_checked(idx, snap_grid);
- }
+ // Expand with the other ones
+ for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ Transform2D xform = canvas_item->get_global_transform_with_canvas();
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 current_rect = canvas_item->_edit_get_rect();
- if (state.has("show_grid")) {
- show_grid = state["show_grid"];
- int idx = view_menu->get_popup()->get_item_index(SHOW_GRID);
- view_menu->get_popup()->set_item_checked(idx, show_grid);
+ rect.expand_to(xform.xform(current_rect.position));
+ rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0)));
+ rect.expand_to(xform.xform(current_rect.position + current_rect.size));
+ rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y)));
+ } else {
+ rect.expand_to(xform.xform(Point2()));
+ }
}
- if (state.has("show_rulers")) {
- show_rulers = state["show_rulers"];
- int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
- view_menu->get_popup()->set_item_checked(idx, show_rulers);
- }
+ return rect;
+}
- if (state.has("show_guides")) {
- show_guides = state["show_guides"];
- int idx = view_menu->get_popup()->get_item_index(SHOW_GUIDES);
- view_menu->get_popup()->set_item_checked(idx, show_guides);
- }
+void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+ if (!p_node)
+ return;
+ if (Object::cast_to<Viewport>(p_node))
+ return;
- if (state.has("show_helpers")) {
- show_helpers = state["show_helpers"];
- int idx = view_menu->get_popup()->get_item_index(SHOW_HELPERS);
- view_menu->get_popup()->set_item_checked(idx, show_helpers);
- }
+ const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
- if (state.has("snap_rotation")) {
- snap_rotation = state["snap_rotation"];
- int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
- snap_config_menu->get_popup()->set_item_checked(idx, snap_rotation);
- }
+ /*bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
+ bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
+ bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
- if (state.has("snap_relative")) {
- snap_relative = state["snap_relative"];
- int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE);
- snap_config_menu->get_popup()->set_item_checked(idx, snap_relative);
- }
+ if (!lock_children && editable) {}*/
- if (state.has("snap_pixel")) {
- snap_pixel = state["snap_pixel"];
- int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
- snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel);
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
+ const CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
+ _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
+ }
}
- if (state.has("skeleton_show_bones")) {
- skeleton_show_bones = state["skeleton_show_bones"];
- int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES);
- skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones);
+ if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
+ Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ if (r_first) {
+ r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
+ r_first = false;
+ }
+ if (r_rect.size != Size2()) {
+ r_rect.expand_to(xform.xform(rect.position));
+ r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
+ r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y)));
+ r_rect.expand_to(xform.xform(rect.position + rect.size));
+ }
+ } else {
+ if (r_first) {
+ r_rect = Rect2(xform.xform(Point2()), Size2());
+ r_first = false;
+ } else {
+ r_rect.expand_to(xform.xform(Point2()));
+ }
+ }
}
-
- viewport->update();
-}
-
-void CanvasItemEditor::_add_canvas_item(CanvasItem *p_canvas_item) {
-
- editor_selection->add_node(p_canvas_item);
-}
-
-void CanvasItemEditor::_remove_canvas_item(CanvasItem *p_canvas_item) {
-
- editor_selection->remove_node(p_canvas_item);
-}
-void CanvasItemEditor::_clear_canvas_items() {
-
- editor_selection->clear();
}
-void CanvasItemEditor::_keying_changed() {
-
- if (AnimationPlayerEditor::singleton->get_key_editor()->is_visible_in_tree())
- animation_hb->show();
- else
- animation_hb->hide();
-}
-
-bool CanvasItemEditor::_is_part_of_subscene(CanvasItem *p_item) {
-
- Node *scene_node = get_tree()->get_edited_scene_root();
- Node *item_owner = p_item->get_owner();
+Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) {
+ Rect2 rect;
+ bool first = true;
+ _expand_encompassing_rect_using_children(rect, p_node, first);
- return item_owner && item_owner != scene_node && p_item != scene_node && item_owner->get_filename() != "";
+ return rect;
}
-void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, Vector<_SelectResult> &r_items, int limit) {
+void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
return;
const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
- CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
-
- if (c && !c->is_set_as_toplevel())
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), p_parent_xform * c->get_transform(), p_canvas_xform, r_items);
- else {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), transform, cl ? cl->get_transform() : p_canvas_xform, r_items); //use base transform
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform);
}
-
- if (limit != 0 && r_items.size() >= limit)
+ if (p_limit != 0 && r_items.size() >= p_limit)
return;
}
- if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) {
-
- Rect2 rect = c->_edit_get_rect();
- Transform2D to_local = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse();
- Point2 local_pos = to_local.xform(p_pos);
- const real_t local_grab_distance = (to_local.xform(p_pos + Vector2(grab_distance, 0)) - local_pos).length();
- Rect2 local_pos_rect = Rect2(local_pos, Vector2(0, 0)).grow(local_grab_distance);
-
- if (rect.intersects(local_pos_rect) && c->_edit_is_selected_on_click(local_pos, local_grab_distance)) {
- Node2D *node = Object::cast_to<Node2D>(c);
+ if (canvas_item && canvas_item->is_visible_in_tree()) {
+ Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse();
+ const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length();
+ if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) {
+ Node2D *node = Object::cast_to<Node2D>(canvas_item);
_SelectResult res;
- res.item = c;
+ res.item = canvas_item;
res.z_index = node ? node->get_z_index() : 0;
res.has_z = node;
r_items.push_back(res);
@@ -651,64 +495,86 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
return;
}
-void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, List<CanvasItem *> *r_items) {
+void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit) {
+
+ Node *scene = editor->get_edited_scene();
+
+ _find_canvas_items_at_pos(p_pos, scene, r_items, p_limit);
+
+ //Remove invalid results
+ for (int i = 0; i < r_items.size(); i++) {
+ Node *node = r_items[i].item;
+
+ // Make sure the selected node is in the current scene, or editable
+ while (node && node != get_tree()->get_edited_scene_root() && node->get_owner() != scene && !scene->is_editable_instance(node->get_owner())) {
+ node = node->get_parent();
+ };
+
+ // Replace the node by the group if grouped
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(node);
+ while (node && node != scene->get_parent()) {
+ CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node);
+ if (canvas_item_tmp && node->has_meta("_edit_group_")) {
+ canvas_item = canvas_item_tmp;
+ }
+ node = node->get_parent();
+ }
+ //Remove the item if invalid
+ if (!canvas_item || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) {
+ r_items.remove(i);
+ i--;
+ } else {
+ r_items[i].item = canvas_item;
+ }
+ }
+}
+
+void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
return;
- CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ Node *scene = editor->get_edited_scene();
- bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
- bool editable = false;
- if (inherited) {
- editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
- }
+ bool editable = p_node == scene || p_node->get_owner() == scene || scene->is_editable_instance(p_node->get_owner());
bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
- if (!lock_children && (!inherited || editable)) {
- for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ bool locked = p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
- if (c && !c->is_set_as_toplevel())
- _find_canvas_items_at_rect(p_rect, p_node->get_child(i), p_parent_xform * c->get_transform(), p_canvas_xform, r_items);
- else {
- CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_rect(p_rect, p_node->get_child(i), transform, cl ? cl->get_transform() : p_canvas_xform, r_items);
+ if (!lock_children || !editable) {
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
+ CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
+ _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
}
}
}
- if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) {
-
- Rect2 rect = c->_edit_get_rect();
- Transform2D xform = p_parent_xform * p_canvas_xform * c->get_transform();
+ if (canvas_item && canvas_item->is_visible_in_tree() && !locked && editable) {
+ Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
- if (p_rect.has_point(xform.xform(rect.position)) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) {
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ if (p_rect.has_point(xform.xform(rect.position)) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) {
- r_items->push_back(c);
+ r_items->push_back(canvas_item);
+ }
+ } else {
+ if (p_rect.has_point(xform.xform(Point2()))) {
+ r_items->push_back(canvas_item);
+ }
}
}
}
-void CanvasItemEditor::_select_click_on_empty_area(Point2 p_click_pos, bool p_append, bool p_box_selection) {
- if (!p_append) {
- editor_selection->clear();
- viewport->update();
- viewport_base->update();
- };
-
- if (p_box_selection) {
- // Start a box selection
- drag_from = transform.affine_inverse().xform(p_click_pos);
- box_selecting = true;
- box_selecting_to = drag_from;
- }
-}
-
-bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag) {
+bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append) {
bool still_selected = true;
if (p_append) {
if (editor_selection->is_selected(item)) {
@@ -717,7 +583,7 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
still_selected = false;
} else {
// Add the item to the selection
- _append_canvas_item(item);
+ editor_selection->add_node(item);
}
} else {
if (!editor_selection->is_selected(item)) {
@@ -730,216 +596,33 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
}
}
}
-
- if (still_selected && p_drag) {
- // Drag the node(s) if requested
- _prepare_drag(p_click_pos);
- }
-
viewport->update();
- viewport_base->update();
return still_selected;
}
-void CanvasItemEditor::_key_move(const Vector2 &p_dir, bool p_snap, KeyMoveMODE p_move_mode) {
-
- if (drag != DRAG_NONE)
- return;
-
- if (editor_selection->get_selected_node_list().empty())
- return;
-
- undo_redo->create_action(TTR("Move Action"), UndoRedo::MERGE_ENDS);
-
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
-
- if (canvas_item->has_meta("_edit_lock_"))
- continue;
-
- Vector2 drag = p_dir;
- if (p_snap)
- drag *= grid_step * Math::pow(2.0, grid_step_multiplier);
-
- undo_redo->add_undo_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
-
- if (p_move_mode == MOVE_VIEW_BASE) {
-
- // drag = transform.affine_inverse().basis_xform(p_dir); // zoom sensitive
- drag = canvas_item->get_global_transform_with_canvas().affine_inverse().basis_xform(drag);
- Rect2 local_rect = canvas_item->_edit_get_rect();
- local_rect.position += drag;
- undo_redo->add_do_method(canvas_item, "_edit_set_rect", local_rect);
-
- } else { // p_move_mode==MOVE_LOCAL_BASE || p_move_mode==MOVE_LOCAL_WITH_ROT
-
- Node2D *node_2d = Object::cast_to<Node2D>(canvas_item);
- if (node_2d) {
-
- if (p_move_mode == MOVE_LOCAL_WITH_ROT) {
- Transform2D m;
- m.rotate(node_2d->get_rotation());
- drag = m.xform(drag);
- }
- node_2d->set_position(node_2d->get_position() + drag);
-
- } else {
- Control *control = Object::cast_to<Control>(canvas_item);
- if (control)
- control->set_position(control->get_position() + drag);
+List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_locked, bool remove_canvas_item_if_parent_in_selection) {
+ List<CanvasItem *> selection;
+ for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (!retreive_locked || !canvas_item->has_meta("_edit_lock_"))) {
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ if (se) {
+ selection.push_back(canvas_item);
}
}
}
- undo_redo->commit_action();
-}
-
-Point2 CanvasItemEditor::_find_topleftmost_point() {
-
- Vector2 tl = Point2(1e10, 1e10);
- Rect2 r2;
- r2.position = tl;
-
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- Rect2 rect = canvas_item->_edit_get_rect();
- Transform2D xform = canvas_item->get_global_transform_with_canvas();
-
- r2.expand_to(xform.xform(rect.position));
- r2.expand_to(xform.xform(rect.position + Vector2(rect.size.x, 0)));
- r2.expand_to(xform.xform(rect.position + rect.size));
- r2.expand_to(xform.xform(rect.position + Vector2(0, rect.size.y)));
- }
-
- return r2.position;
-}
-
-int CanvasItemEditor::get_item_count() {
-
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- int ic = 0;
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
-
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- ic++;
- };
-
- return ic;
-}
-
-CanvasItem *CanvasItemEditor::_get_single_item() {
-
- Map<Node *, Object *> &selection = editor_selection->get_selection();
-
- CanvasItem *single_item = NULL;
-
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- if (single_item)
- return NULL; //morethan one
-
- single_item = canvas_item;
- };
-
- return single_item;
-}
-
-CanvasItemEditor::DragType CanvasItemEditor::_get_resize_handle_drag_type(const Point2 &p_click, Vector2 &r_point) {
- // Returns a drag type if a resize handle is clicked
- CanvasItem *canvas_item = _get_single_item();
-
- ERR_FAIL_COND_V(!canvas_item, DRAG_NONE);
-
- Rect2 rect = canvas_item->_edit_get_rect();
- Transform2D xforml = canvas_item->get_global_transform_with_canvas();
- Transform2D xform = transform * xforml;
-
- Vector2 endpoints[4] = {
-
- xform.xform(rect.position),
- xform.xform(rect.position + Vector2(rect.size.x, 0)),
- xform.xform(rect.position + rect.size),
- xform.xform(rect.position + Vector2(0, rect.size.y))
- };
-
- Vector2 endpointsl[4] = {
-
- xforml.xform(rect.position),
- xforml.xform(rect.position + Vector2(rect.size.x, 0)),
- xforml.xform(rect.position + rect.size),
- xforml.xform(rect.position + Vector2(0, rect.size.y))
- };
-
- DragType dragger[] = {
- DRAG_TOP_LEFT,
- DRAG_TOP,
- DRAG_TOP_RIGHT,
- DRAG_RIGHT,
- DRAG_BOTTOM_RIGHT,
- DRAG_BOTTOM,
- DRAG_BOTTOM_LEFT,
- DRAG_LEFT
- };
-
- float radius = (select_handle->get_size().width / 2) * 1.5;
-
- for (int i = 0; i < 4; i++) {
-
- int prev = (i + 3) % 4;
- int next = (i + 1) % 4;
-
- r_point = endpointsl[i];
-
- Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
- ofs *= 1.4144 * (select_handle->get_size().width / 2);
-
- ofs += endpoints[i];
-
- if (ofs.distance_to(p_click) < radius)
- return dragger[i * 2];
-
- ofs = (endpoints[i] + endpoints[next]) / 2;
- ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2);
-
- r_point = (endpointsl[i] + endpointsl[next]) / 2;
-
- if (ofs.distance_to(p_click) < radius)
- return dragger[i * 2 + 1];
+ if (remove_canvas_item_if_parent_in_selection) {
+ List<CanvasItem *> filtered_selection;
+ for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+ if (!selection.find(E->get()->get_parent())) {
+ filtered_selection.push_back(E->get());
+ }
+ }
+ return filtered_selection;
+ } else {
+ return selection;
}
-
- return DRAG_NONE;
}
Vector2 CanvasItemEditor::_anchor_to_position(const Control *p_control, Vector2 anchor) {
@@ -958,148 +641,93 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2
return p_control->get_transform().xform(position) / parent_size;
}
-CanvasItemEditor::DragType CanvasItemEditor::_get_anchor_handle_drag_type(const Point2 &p_click, Vector2 &r_point) {
- // Returns a drag type if an anchor handle is clicked
- CanvasItem *canvas_item = _get_single_item();
- ERR_FAIL_COND_V(!canvas_item, DRAG_NONE);
-
- Control *control = Object::cast_to<Control>(canvas_item);
- ERR_FAIL_COND_V(!control, DRAG_NONE);
-
- Vector2 anchor_pos[4];
- anchor_pos[0] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_TOP));
- anchor_pos[1] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_TOP));
- anchor_pos[2] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_BOTTOM));
- anchor_pos[3] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_BOTTOM));
-
- Rect2 anchor_rects[4];
- for (int i = 0; i < 4; i++) {
- anchor_pos[i] = (transform * control->get_global_transform_with_canvas()).xform(_anchor_to_position(control, anchor_pos[i]));
- anchor_rects[i] = Rect2(anchor_pos[i], anchor_handle->get_size());
- anchor_rects[i].position -= anchor_handle->get_size() * Vector2(i == 0 || i == 3, i <= 1);
- }
-
- DragType dragger[] = {
- DRAG_ANCHOR_TOP_LEFT,
- DRAG_ANCHOR_TOP_RIGHT,
- DRAG_ANCHOR_BOTTOM_RIGHT,
- DRAG_ANCHOR_BOTTOM_LEFT,
- };
-
- for (int i = 0; i < 4; i++) {
- if (anchor_rects[i].has_point(p_click)) {
- r_point = transform.affine_inverse().xform(anchor_pos[i]);
- if ((anchor_pos[0] == anchor_pos[2]) && (anchor_pos[0].distance_to(p_click) < anchor_handle->get_size().length() / 3.0)) {
- return DRAG_ANCHOR_ALL;
+void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) {
+ for (List<CanvasItem *>::Element *E = p_canvas_items.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ if (se) {
+ se->undo_state = canvas_item->_edit_get_state();
+ se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
+ if (canvas_item->_edit_use_rect()) {
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
} else {
- return dragger[i];
+ se->pre_drag_rect = Rect2();
+ }
+ se->pre_drag_bones_length = List<float>();
+ se->pre_drag_bones_undo_state = List<Dictionary>();
+
+ // If we have a bone, save the state of all nodes in the IK chain
+ Node2D *bone = Object::cast_to<Node2D>(canvas_item);
+ if (bone && bone->has_meta("_edit_bone_")) {
+ // Check if we have an IK chain
+ List<Node2D *> bone_ik_list;
+ bool ik_found = false;
+ bone = Object::cast_to<Node2D>(bone->get_parent());
+ while (bone) {
+ bone_ik_list.push_back(bone);
+ if (bone->has_meta("_edit_ik_")) {
+ ik_found = true;
+ break;
+ } else if (!bone->has_meta("_edit_bone_")) {
+ break;
+ }
+ bone = Object::cast_to<Node2D>(bone->get_parent());
+ }
+
+ //Save the bone state and length if we have an IK chain
+ if (ik_found) {
+ bone = Object::cast_to<Node2D>(canvas_item);
+ Transform2D bone_xform = bone->get_global_transform();
+ for (List<Node2D *>::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) {
+ bone_xform = bone_xform * bone->get_transform().affine_inverse();
+ Node2D *parent_bone = bone_E->get();
+ se->pre_drag_bones_length.push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position()));
+ se->pre_drag_bones_undo_state.push_back(parent_bone->_edit_get_state());
+ bone = parent_bone;
+ }
+ }
}
}
}
-
- return DRAG_NONE;
}
-void CanvasItemEditor::_prepare_drag(const Point2 &p_click_pos) {
-
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
+void CanvasItemEditor::_restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones) {
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
-
- se->undo_state = canvas_item->_edit_get_state();
- if (Object::cast_to<Node2D>(canvas_item))
- se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot();
- if (Object::cast_to<Control>(canvas_item))
- se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
-
- se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->_edit_get_rect();
- }
-
- if (selection.size() == 1 && Object::cast_to<Node2D>(selection[0]) && bone_ik_list.size() == 0) {
- drag = DRAG_NODE_2D;
- drag_point_from = Object::cast_to<Node2D>(selection[0])->get_global_position();
- } else {
- drag = DRAG_ALL;
- drag_point_from = _find_topleftmost_point();
- }
- drag_from = transform.affine_inverse().xform(p_click_pos);
-}
-
-void CanvasItemEditor::incbeg(float &beg, float &end, float inc, float minsize, bool p_symmetric) {
-
- if (minsize < 0) {
-
- beg += inc;
- if (p_symmetric)
- end -= inc;
- } else {
-
- if (p_symmetric) {
- beg += inc;
- end -= inc;
- if (end - beg < minsize) {
- float center = (beg + end) / 2.0;
- beg = center - minsize / 2.0;
- end = center + minsize / 2.0;
+ canvas_item->_edit_set_state(se->undo_state);
+ if (restore_bones) {
+ for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) {
+ canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent());
+ canvas_item->_edit_set_state(E->get());
}
-
- } else {
- if (end - (beg + inc) < minsize)
- beg = end - minsize;
- else
- beg += inc;
}
}
}
-void CanvasItemEditor::incend(float &beg, float &end, float inc, float minsize, bool p_symmetric) {
-
- if (minsize < 0) {
-
- end += inc;
- if (p_symmetric)
- beg -= inc;
- } else {
-
- if (p_symmetric) {
-
- end += inc;
- beg -= inc;
- if (end - beg < minsize) {
- float center = (beg + end) / 2.0;
- beg = center - minsize / 2.0;
- end = center + minsize / 2.0;
+void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones) {
+ undo_redo->create_action(action_name);
+ for (List<CanvasItem *>::Element *E = p_canvas_items.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
+ undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state);
+ if (commit_bones) {
+ for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) {
+ canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent());
+ undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
+ undo_redo->add_undo_method(canvas_item, "_edit_set_state", E->get());
}
-
- } else {
- if ((end + inc) - beg < minsize)
- end = beg + minsize;
- else
- end += inc;
}
}
-}
-
-void CanvasItemEditor::_append_canvas_item(CanvasItem *p_item) {
-
- editor_selection->add_node(p_item);
+ undo_redo->add_do_method(viewport, "update");
+ undo_redo->add_undo_method(viewport, "update");
+ undo_redo->commit_action();
}
void CanvasItemEditor::_snap_changed() {
((SnapDialog *)snap_dialog)->get_fields(grid_offset, grid_step, snap_rotation_offset, snap_rotation_step);
grid_step_multiplier = 0;
- viewport_base->update();
viewport->update();
}
@@ -1111,7 +739,7 @@ void CanvasItemEditor::_selection_result_pressed(int p_result) {
CanvasItem *item = selection_results[p_result].item;
if (item)
- _select_click_on_item(item, Point2(), additive_selection, false);
+ _select_click_on_item(item, Point2(), selection_menu_additive_selection);
}
void CanvasItemEditor::_selection_menu_hide() {
@@ -1121,121 +749,13 @@ void CanvasItemEditor::_selection_menu_hide() {
selection_menu->set_size(Vector2(0, 0));
}
-void CanvasItemEditor::_list_select(const Ref<InputEventMouseButton> &b) {
-
- Point2 click = viewport_scrollable->get_transform().affine_inverse().xform(b->get_position());
-
- Node *scene = editor->get_edited_scene();
- if (!scene)
- return;
-
- _find_canvas_items_at_pos(click, scene, transform, Transform2D(), selection_results);
-
- for (int i = 0; i < selection_results.size(); i++) {
- CanvasItem *item = selection_results[i].item;
- if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) {
- //invalid result
- selection_results.remove(i);
- i--;
- }
- }
-
- if (selection_results.size() == 1) {
-
- CanvasItem *item = selection_results[0].item;
- selection_results.clear();
-
- additive_selection = b->get_shift();
-
- if (!_select_click_on_item(item, click, additive_selection, false))
- return;
-
- } else if (!selection_results.empty()) {
-
- selection_results.sort();
-
- NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
- StringName root_name = root_path.get_name(root_path.get_name_count() - 1);
-
- for (int i = 0; i < selection_results.size(); i++) {
-
- CanvasItem *item = selection_results[i].item;
-
- Ref<Texture> icon;
- if (item->has_meta("_editor_icon"))
- icon = item->get_meta("_editor_icon");
- else
- icon = get_icon(has_icon(item->get_class(), "EditorIcons") ? item->get_class() : String("Object"), "EditorIcons");
-
- String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path());
-
- selection_menu->add_item(item->get_name());
- selection_menu->set_item_icon(i, icon);
- selection_menu->set_item_metadata(i, node_path);
- selection_menu->set_item_tooltip(i, String(item->get_name()) + "\nType: " + item->get_class() + "\nPath: " + node_path);
- }
-
- additive_selection = b->get_shift();
-
- selection_menu->set_global_position(b->get_global_position());
- selection_menu->popup();
- selection_menu->call_deferred("grab_click_focus");
- selection_menu->set_invalidate_click_until_motion();
-
- return;
- }
-}
-
-void CanvasItemEditor::_update_cursor() {
-
- CursorShape c = CURSOR_ARROW;
- switch (drag) {
- case DRAG_NONE:
- if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
- c = CURSOR_DRAG;
- } else {
- switch (tool) {
- case TOOL_MOVE:
- c = CURSOR_MOVE;
- break;
- case TOOL_EDIT_PIVOT:
- c = CURSOR_CROSS;
- break;
- case TOOL_PAN:
- c = CURSOR_DRAG;
- break;
- }
- }
- break;
- case DRAG_LEFT:
- case DRAG_RIGHT:
- c = CURSOR_HSIZE;
- break;
- case DRAG_TOP:
- case DRAG_BOTTOM:
- c = CURSOR_VSIZE;
- break;
- case DRAG_TOP_LEFT:
- case DRAG_BOTTOM_RIGHT:
- c = CURSOR_FDIAGSIZE;
- break;
- case DRAG_TOP_RIGHT:
- case DRAG_BOTTOM_LEFT:
- c = CURSOR_BDIAGSIZE;
- break;
- case DRAG_ALL:
- case DRAG_NODE_2D:
- c = CURSOR_MOVE;
- break;
- }
- viewport->set_default_cursor_shape(c);
-}
-
-void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event) {
-
+bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> b = p_event;
- if (b.is_valid()) {
- if (b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
+ Ref<InputEventMouseMotion> m = p_event;
+
+ // Start dragging a guide
+ if (drag_type == DRAG_NONE) {
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
if (show_guides && show_rulers && EditorNode::get_singleton()->get_edited_scene()) {
Transform2D xform = viewport_scrollable->get_transform() * transform;
// Retrieve the guide lists
@@ -1251,48 +771,64 @@ void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event)
// Press button
if (b->get_position().x < RULER_WIDTH && b->get_position().y < RULER_WIDTH) {
// Drag a new double guide
- drag = DRAG_DOUBLE_GUIDE;
- edited_guide_index = -1;
+ drag_type = DRAG_DOUBLE_GUIDE;
+ dragged_guide_index = -1;
+ return true;
} else if (b->get_position().x < RULER_WIDTH) {
// Check if we drag an existing horizontal guide
float minimum = 1e20;
- edited_guide_index = -1;
+ dragged_guide_index = -1;
for (int i = 0; i < hguides.size(); i++) {
if (ABS(xform.xform(Point2(0, hguides[i])).y - b->get_position().y) < MIN(minimum, 8)) {
- edited_guide_index = i;
+ dragged_guide_index = i;
}
}
- if (edited_guide_index >= 0) {
+ if (dragged_guide_index >= 0) {
// Drag an existing horizontal guide
- drag = DRAG_H_GUIDE;
+ drag_type = DRAG_H_GUIDE;
} else {
// Drag a new vertical guide
- drag = DRAG_V_GUIDE;
+ drag_type = DRAG_V_GUIDE;
}
+ return true;
} else if (b->get_position().y < RULER_WIDTH) {
// Check if we drag an existing vertical guide
float minimum = 1e20;
- edited_guide_index = -1;
+ dragged_guide_index = -1;
for (int i = 0; i < vguides.size(); i++) {
if (ABS(xform.xform(Point2(vguides[i], 0)).x - b->get_position().x) < MIN(minimum, 8)) {
- edited_guide_index = i;
+ dragged_guide_index = i;
}
}
- if (edited_guide_index >= 0) {
+ if (dragged_guide_index >= 0) {
// Drag an existing vertical guide
- drag = DRAG_V_GUIDE;
+ drag_type = DRAG_V_GUIDE;
} else {
// Drag a new vertical guide
- drag = DRAG_H_GUIDE;
+ drag_type = DRAG_H_GUIDE;
}
+ drag_from = xform.affine_inverse().xform(b->get_position());
+ return true;
}
}
}
+ }
+
+ if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_V_GUIDE || drag_type == DRAG_H_GUIDE) {
+ // Move the guide
+ if (m.is_valid()) {
+ Transform2D xform = viewport_scrollable->get_transform() * transform;
+ drag_to = xform.affine_inverse().xform(m->get_position());
+
+ dragged_guide_pos = xform.xform(snap_point(drag_to, SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES));
+ viewport->update();
+ return true;
+ }
- if (b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
- // Release button
+ // Release confirms the guide move
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
if (show_guides && EditorNode::get_singleton()->get_edited_scene()) {
Transform2D xform = viewport_scrollable->get_transform() * transform;
@@ -1307,65 +843,65 @@ void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event)
}
Point2 edited = snap_point(xform.affine_inverse().xform(b->get_position()), SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES);
- if (drag == DRAG_V_GUIDE) {
+ if (drag_type == DRAG_V_GUIDE) {
Array prev_vguides = vguides.duplicate();
if (b->get_position().x > RULER_WIDTH) {
// Adds a new vertical guide
- if (edited_guide_index >= 0) {
- vguides[edited_guide_index] = edited.x;
+ if (dragged_guide_index >= 0) {
+ vguides[dragged_guide_index] = edited.x;
undo_redo->create_action(TTR("Move vertical guide"));
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
} else {
vguides.push_back(edited.x);
undo_redo->create_action(TTR("Create new vertical guide"));
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
}
} else {
- if (edited_guide_index >= 0) {
- vguides.remove(edited_guide_index);
+ if (dragged_guide_index >= 0) {
+ vguides.remove(dragged_guide_index);
undo_redo->create_action(TTR("Remove vertical guide"));
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", vguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
}
}
- } else if (drag == DRAG_H_GUIDE) {
+ } else if (drag_type == DRAG_H_GUIDE) {
Array prev_hguides = hguides.duplicate();
if (b->get_position().y > RULER_WIDTH) {
// Adds a new horizontal guide
- if (edited_guide_index >= 0) {
- hguides[edited_guide_index] = edited.y;
+ if (dragged_guide_index >= 0) {
+ hguides[dragged_guide_index] = edited.y;
undo_redo->create_action(TTR("Move horizontal guide"));
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
} else {
hguides.push_back(edited.y);
undo_redo->create_action(TTR("Create new horizontal guide"));
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
}
} else {
- if (edited_guide_index >= 0) {
- hguides.remove(edited_guide_index);
+ if (dragged_guide_index >= 0) {
+ hguides.remove(dragged_guide_index);
undo_redo->create_action(TTR("Remove horizontal guide"));
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
}
}
- } else if (drag == DRAG_DOUBLE_GUIDE) {
+ } else if (drag_type == DRAG_DOUBLE_GUIDE) {
Array prev_hguides = hguides.duplicate();
Array prev_vguides = vguides.duplicate();
if (b->get_position().x > RULER_WIDTH && b->get_position().y > RULER_WIDTH) {
@@ -1377,880 +913,1089 @@ void CanvasItemEditor::_gui_input_viewport_base(const Ref<InputEvent> &p_event)
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", hguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_vertical_guides_", prev_vguides);
undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_meta", "_edit_horizontal_guides_", prev_hguides);
- undo_redo->add_undo_method(viewport_base, "update");
+ undo_redo->add_undo_method(viewport, "update");
undo_redo->commit_action();
}
}
}
- if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_V_GUIDE || drag == DRAG_H_GUIDE) {
- drag = DRAG_NONE;
- viewport_base->update();
- }
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
}
}
+ return false;
+}
- Ref<InputEventMouseMotion> m = p_event;
- if (m.is_valid()) {
- if (!viewport_base->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) {
- viewport_base->call_deferred("grab_focus");
+bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
+ if (b.is_valid()) {
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_DOWN) {
+ // Scroll or pan down
+ if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
+ view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
+ _update_scrollbars();
+ viewport->update();
+ } else {
+ _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position());
+ }
+ return true;
}
- if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_H_GUIDE || drag == DRAG_V_GUIDE) {
- Transform2D xform = viewport_scrollable->get_transform() * transform;
- Point2 mouse_pos = m->get_position();
- mouse_pos = xform.affine_inverse().xform(mouse_pos);
- mouse_pos = xform.xform(snap_point(mouse_pos, SNAP_GRID | SNAP_PIXEL | SNAP_OTHER_NODES));
- edited_guide_pos = mouse_pos;
- viewport_base->update();
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_UP) {
+ // Scroll or pan up
+ if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
+ view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
+ _update_scrollbars();
+ viewport->update();
+ } else {
+ _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position());
+ }
+ return true;
}
- }
- Ref<InputEventKey> k = p_event;
- if (k.is_valid()) {
- if (k->is_pressed() && drag == DRAG_NONE) {
- // Move the object with the arrow keys
- KeyMoveMODE move_mode = MOVE_VIEW_BASE;
- if (k->get_alt()) move_mode = MOVE_LOCAL_BASE;
- if (k->get_control() || k->get_metakey()) move_mode = MOVE_LOCAL_WITH_ROT;
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_LEFT) {
+ // Pan left
+ if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
+ view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
+ _update_scrollbars();
+ viewport->update();
+ return true;
+ }
+ }
- if (k->get_scancode() == KEY_UP)
- _key_move(Vector2(0, -1), k->get_shift(), move_mode);
- else if (k->get_scancode() == KEY_DOWN)
- _key_move(Vector2(0, 1), k->get_shift(), move_mode);
- else if (k->get_scancode() == KEY_LEFT)
- _key_move(Vector2(-1, 0), k->get_shift(), move_mode);
- else if (k->get_scancode() == KEY_RIGHT)
- _key_move(Vector2(1, 0), k->get_shift(), move_mode);
- else if (k->get_scancode() == KEY_ESCAPE) {
- editor_selection->clear();
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_RIGHT) {
+ // Pan right
+ if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
+ view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
+ _update_scrollbars();
viewport->update();
- } else
- return;
+ return true;
+ }
+ }
- accept_event();
+ if (drag_type == DRAG_NONE) {
+ if (b->is_pressed() &&
+ (b->get_button_index() == BUTTON_MIDDLE ||
+ (b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) ||
+ (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) {
+ // Pan the viewport
+ drag_type = DRAG_PAN;
+ }
}
- }
-}
-void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
+ if (drag_type == DRAG_PAN) {
+ if (!b->is_pressed()) {
+ // Stop panning the viewport (for any mouse button press)
+ drag_type = DRAG_NONE;
+ }
+ }
+ }
- {
- EditorNode *en = editor;
- EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid()) {
+ if (k->get_scancode() == KEY_SPACE && EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning")) {
+ if (drag_type == DRAG_NONE) {
+ if (k->is_pressed() && !k->is_echo()) {
+ //Pan the viewport
+ drag_type = DRAG_PAN;
+ }
+ } else if (drag_type == DRAG_PAN) {
+ if (!k->is_pressed()) {
+ // Stop panning the viewport (for any mouse button press)
+ drag_type = DRAG_NONE;
+ }
+ }
+ }
+ }
- if (!over_plugin_list->empty()) {
- bool discard = over_plugin_list->forward_gui_input(p_event);
- if (discard) {
- accept_event();
- return;
+ Ref<InputEventMouseMotion> m = p_event;
+ if (m.is_valid()) {
+ if (drag_type == DRAG_PAN) {
+ // Pan the viewport
+ Point2i relative;
+ if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) {
+ relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect());
+ } else {
+ relative = m->get_relative();
}
+ view_offset.x -= relative.x / zoom;
+ view_offset.y -= relative.y / zoom;
+ _update_scrollbars();
+ viewport->update();
+ return true;
}
}
Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
if (magnify_gesture.is_valid()) {
-
+ // Zoom gesture
_zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position());
- return;
+ return true;
}
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
-
+ // Pan gesture
const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta();
- h_scroll->set_value(h_scroll->get_value() + delta.x);
- v_scroll->set_value(v_scroll->get_value() + delta.y);
- return;
+ view_offset.x += delta.x;
+ view_offset.y += delta.y;
+ _update_scrollbars();
+ viewport->update();
+ return true;
}
+ return false;
+}
+
+bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseMotion> m = p_event;
Ref<InputEventMouseButton> b = p_event;
- if (b.is_valid()) {
- // Button event
+ Ref<InputEventKey> k = p_event;
- if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
- // Scroll or pan down
- if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
- v_scroll->set_value(v_scroll->get_value() + int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor());
- _update_scroll(0);
- viewport->update();
- } else {
- _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position());
+ // Drag the pivot (in pivot mode / with V key)
+ if (drag_type == DRAG_NONE) {
+ if ((b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) ||
+ (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_scancode() == KEY_V)) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+
+ // Filters the selection with nodes that allow setting the pivot
+ drag_selection = List<CanvasItem *>();
+ for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ if (canvas_item->_edit_use_pivot()) {
+ drag_selection.push_back(canvas_item);
+ }
}
- return;
- }
+ // Start dragging if we still have nodes
+ if (drag_selection.size() > 0) {
+ drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position());
+ Vector2 new_pos;
+ if (drag_selection.size() == 1)
+ new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, drag_selection[0]);
+ else
+ new_pos = snap_point(drag_from, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL);
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ canvas_item->_edit_set_pivot(canvas_item->get_global_transform_with_canvas().affine_inverse().xform(new_pos));
+ }
- if (b->get_button_index() == BUTTON_WHEEL_UP) {
- // Scroll or pan up
- if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
- v_scroll->set_value(v_scroll->get_value() - int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor());
- _update_scroll(0);
- viewport->update();
- } else {
- _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position());
+ drag_type = DRAG_PIVOT;
+ _save_canvas_item_state(drag_selection);
}
+ return true;
+ }
+ }
- return;
+ if (drag_type == DRAG_PIVOT) {
+ // Move the pivot
+ if (m.is_valid()) {
+ drag_to = transform.affine_inverse().xform(m->get_position());
+ _restore_canvas_item_state(drag_selection);
+ Vector2 new_pos;
+ if (drag_selection.size() == 1)
+ new_pos = snap_point(drag_to, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, drag_selection[0]);
+ else
+ new_pos = snap_point(drag_to, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL);
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ canvas_item->_edit_set_pivot(canvas_item->get_global_transform_with_canvas().affine_inverse().xform(new_pos));
+ }
+ return true;
}
- if (b->get_button_index() == BUTTON_WHEEL_LEFT) {
- // Pan left
- if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
+ // Confirm the pivot move
+ if ((b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) ||
+ (k.is_valid() && !k->is_pressed() && k->get_scancode() == KEY_V)) {
+ _commit_canvas_item_state(drag_selection, TTR("Move pivot"));
+ drag_type = DRAG_NONE;
+ return true;
+ }
- h_scroll->set_value(h_scroll->get_value() - int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor());
- }
+ // Cancel a drag
+ if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+ _restore_canvas_item_state(drag_selection);
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
}
+ }
+ return false;
+}
- if (b->get_button_index() == BUTTON_WHEEL_RIGHT) {
- // Pan right
- if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
+void CanvasItemEditor::_solve_IK(Node2D *leaf_node, Point2 target_position) {
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(leaf_node);
+ if (se && !se->pre_drag_bones_undo_state.empty()) {
+
+ // Build the node list
+ Point2 leaf_pos = target_position;
+
+ List<Node2D *> joints_list;
+ List<Point2> joints_pos;
+ Node2D *joint = leaf_node;
+ Transform2D joint_transform = leaf_node->get_global_transform_with_canvas();
+ for (int i = 0; i < se->pre_drag_bones_undo_state.size() + 1; i++) {
+ joints_list.push_back(joint);
+ joints_pos.push_back(joint_transform.get_origin());
+ joint_transform = joint_transform * joint->get_transform().affine_inverse();
+ joint = Object::cast_to<Node2D>(joint->get_parent());
+ }
+ Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin();
- h_scroll->set_value(h_scroll->get_value() + int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor());
+ // Restraints the node to a maximum distance is necessary
+ float total_len = 0;
+ for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) {
+ total_len += E->get();
+ }
+ if ((root_pos.distance_to(leaf_pos)) > total_len) {
+ Vector2 rel = leaf_pos - root_pos;
+ rel = rel.normalized() * total_len;
+ leaf_pos = root_pos + rel;
+ }
+ joints_pos[0] = leaf_pos;
+
+ // Run the solver
+ int solver_iterations = 64;
+ float solver_k = 0.3;
+
+ // Build the position list
+ for (int i = 0; i < solver_iterations; i++) {
+ // Handle the leaf joint
+ int node_id = 0;
+ for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) {
+ Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized();
+ int len = E->get();
+ if (E == se->pre_drag_bones_length.front()) {
+ joints_pos[1] = joints_pos[1].linear_interpolate(joints_pos[0] + len * direction, solver_k);
+ } else if (E == se->pre_drag_bones_length.back()) {
+ joints_pos[node_id] = joints_pos[node_id].linear_interpolate(joints_pos[node_id + 1] - len * direction, solver_k);
+ } else {
+ Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0;
+ joints_pos[node_id] = joints_pos[node_id].linear_interpolate(center - (direction * len) / 2.0, solver_k);
+ joints_pos[node_id + 1] = joints_pos[node_id + 1].linear_interpolate(center + (direction * len) / 2.0, solver_k);
+ }
+ node_id++;
}
}
- if (b->get_button_index() == BUTTON_RIGHT) {
-
- if (b->is_pressed() && (tool == TOOL_SELECT && b->get_alt())) {
- // Open the selection list
- _list_select(b);
- return;
+ // Set the position
+ float total_rot = 0.0f;
+ for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) {
+ Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized();
+ Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized();
+ float rot = current.angle_to(target);
+ if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) {
+ rot = -rot;
}
+ joints_list[node_id]->rotate(rot);
+ total_rot += rot;
+ }
- if (get_item_count() > 0 && drag != DRAG_NONE) {
- // Cancel a drag
- if (bone_ik_list.size()) {
- for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) {
- E->get().node->_edit_set_state(E->get().orig_state);
- }
+ joints_list[0]->rotate(-total_rot);
+ }
+}
- bone_ik_list.clear();
+bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseMotion> m = p_event;
+ // Start rotation
+ if (drag_type == DRAG_NONE) {
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
+ drag_selection = _get_edited_canvas_items();
+ if (drag_selection.size() > 0 && ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) {
+ drag_type = DRAG_ROTATE;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ CanvasItem *canvas_item = drag_selection[0];
+ if (canvas_item->_edit_use_pivot()) {
+ drag_rotation_center = canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_pivot());
} else {
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
-
- canvas_item->_edit_set_state(se->undo_state);
- if (Object::cast_to<Node2D>(canvas_item))
- Object::cast_to<Node2D>(canvas_item)->_edit_set_pivot(se->undo_pivot);
- if (Object::cast_to<Control>(canvas_item))
- Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot);
- }
+ drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin();
}
+ _save_canvas_item_state(drag_selection);
+ return true;
+ }
+ }
+ }
- drag = DRAG_NONE;
- viewport->update();
- can_move_pivot = false;
-
- } else if (box_selecting) {
- // Cancel box selection
- box_selecting = false;
+ if (drag_type == DRAG_ROTATE) {
+ // Rotate the node
+ if (m.is_valid()) {
+ _restore_canvas_item_state(drag_selection);
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ drag_to = transform.affine_inverse().xform(m->get_position());
+ canvas_item->_edit_set_rotation(snap_angle(canvas_item->_edit_get_rotation() + (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), canvas_item->_edit_get_rotation()));
viewport->update();
}
- return;
+ return true;
}
- if (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT) {
- if (b->is_pressed())
- // Open the selection list
- _list_select(b);
- return;
+ // Confirms the node rotation
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
+ _commit_canvas_item_state(drag_selection, TTR("Rotate CanvasItem"));
+ drag_type = DRAG_NONE;
+ return true;
}
- if (b->get_button_index() == BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) {
- if (b->is_pressed()) {
- // Set the pivot point
- Point2 mouse_pos = b->get_position();
- mouse_pos = transform.affine_inverse().xform(mouse_pos);
- mouse_pos = snap_point(mouse_pos, SNAP_DEFAULT, _get_single_item());
- _edit_set_pivot(mouse_pos);
- }
- return;
+ // Cancel a drag
+ if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+ _restore_canvas_item_state(drag_selection);
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
}
+ }
+ return false;
+}
- if (tool == TOOL_PAN || b->get_button_index() != BUTTON_LEFT || Input::get_singleton()->is_key_pressed(KEY_SPACE))
- // Pan the view
- return;
-
- // -- From now we consider that the button is BUTTON_LEFT --
-
- if (!b->is_pressed()) {
-
- if (drag != DRAG_NONE) {
- // Stop dragging
- if (undo_redo) {
+bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
- if (bone_ik_list.size()) {
- undo_redo->create_action(TTR("Edit IK Chain"));
+ // Open a sub-scene on double-click
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && b->is_doubleclick() && tool == TOOL_SELECT) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ if (selection.size() == 1) {
+ CanvasItem *canvas_item = selection[0];
+ if (canvas_item->get_filename() != "" && canvas_item != editor->get_edited_scene()) {
+ editor->open_request(canvas_item->get_filename());
+ return true;
+ }
+ }
+ }
+ return false;
+}
- for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) {
+bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseMotion> m = p_event;
- undo_redo->add_do_method(E->get().node, "_edit_set_state", E->get().node->_edit_get_state());
- undo_redo->add_undo_method(E->get().node, "_edit_set_state", E->get().orig_state);
- }
+ // Starts anchor dragging if needed
+ if (drag_type == DRAG_NONE) {
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT && show_helpers) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ if (selection.size() == 1) {
+ Control *control = Object::cast_to<Control>(selection[0]);
+ if (control && !Object::cast_to<Container>(control->get_parent())) {
+ Vector2 anchor_pos[4];
+ anchor_pos[0] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_TOP));
+ anchor_pos[1] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_TOP));
+ anchor_pos[2] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_BOTTOM));
+ anchor_pos[3] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_BOTTOM));
- undo_redo->add_do_method(viewport, "update");
- undo_redo->add_undo_method(viewport, "update");
+ Rect2 anchor_rects[4];
+ for (int i = 0; i < 4; i++) {
+ anchor_pos[i] = (transform * control->get_global_transform_with_canvas()).xform(_anchor_to_position(control, anchor_pos[i]));
+ anchor_rects[i] = Rect2(anchor_pos[i], anchor_handle->get_size());
+ anchor_rects[i].position -= anchor_handle->get_size() * Vector2(i == 0 || i == 3, i <= 1);
+ }
- bone_ik_list.clear();
+ DragType dragger[] = {
+ DRAG_ANCHOR_TOP_LEFT,
+ DRAG_ANCHOR_TOP_RIGHT,
+ DRAG_ANCHOR_BOTTOM_RIGHT,
+ DRAG_ANCHOR_BOTTOM_LEFT,
+ };
- undo_redo->commit_action();
- } else {
- undo_redo->create_action(TTR("Edit CanvasItem"));
-
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
-
- Variant state = canvas_item->_edit_get_state();
- undo_redo->add_do_method(canvas_item, "_edit_set_state", state);
- undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state);
- {
- Node2D *pvt = Object::cast_to<Node2D>(canvas_item);
- if (pvt && pvt->_edit_use_pivot()) {
- undo_redo->add_do_method(canvas_item, "_edit_set_pivot", pvt->_edit_get_pivot());
- undo_redo->add_undo_method(canvas_item, "_edit_set_pivot", se->undo_pivot);
- }
-
- Control *cnt = Object::cast_to<Control>(canvas_item);
- if (cnt) {
- undo_redo->add_do_method(canvas_item, "set_pivot_offset", cnt->get_pivot_offset());
- undo_redo->add_undo_method(canvas_item, "set_pivot_offset", se->undo_pivot);
- }
+ for (int i = 0; i < 4; i++) {
+ if (anchor_rects[i].has_point(b->get_position())) {
+ if ((anchor_pos[0] == anchor_pos[2]) && (anchor_pos[0].distance_to(b->get_position()) < anchor_handle->get_size().length() / 3.0)) {
+ drag_type = DRAG_ANCHOR_ALL;
+ } else {
+ drag_type = dragger[i];
}
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = List<CanvasItem *>();
+ drag_selection.push_back(control);
+ _save_canvas_item_state(drag_selection);
+ return true;
}
- undo_redo->commit_action();
}
}
-
- drag = DRAG_NONE;
- viewport->update();
- can_move_pivot = false;
}
+ }
+ }
- if (box_selecting) {
- // Stop box selection
- Node *scene = editor->get_edited_scene();
- if (scene) {
+ if (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_TOP_RIGHT || drag_type == DRAG_ANCHOR_BOTTOM_RIGHT || drag_type == DRAG_ANCHOR_BOTTOM_LEFT || drag_type == DRAG_ANCHOR_ALL) {
+ // Drag the anchor
+ if (m.is_valid()) {
+ _restore_canvas_item_state(drag_selection);
+ Control *control = Object::cast_to<Control>(drag_selection[0]);
- List<CanvasItem *> selitems;
+ drag_to = transform.affine_inverse().xform(m->get_position());
- Point2 bsfrom = transform.xform(drag_from);
- Point2 bsto = transform.xform(box_selecting_to);
- if (bsfrom.x > bsto.x)
- SWAP(bsfrom.x, bsto.x);
- if (bsfrom.y > bsto.y)
- SWAP(bsfrom.y, bsto.y);
+ Transform2D xform = control->get_global_transform_with_canvas().affine_inverse();
- _find_canvas_items_at_rect(Rect2(bsfrom, bsto - bsfrom), scene, transform, Transform2D(), &selitems);
+ Point2 previous_anchor;
+ previous_anchor.x = (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_BOTTOM_LEFT) ? control->get_anchor(MARGIN_LEFT) : control->get_anchor(MARGIN_RIGHT);
+ previous_anchor.y = (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_TOP_RIGHT) ? control->get_anchor(MARGIN_TOP) : control->get_anchor(MARGIN_BOTTOM);
+ previous_anchor = xform.affine_inverse().xform(_anchor_to_position(control, previous_anchor));
- for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) {
+ Vector2 new_anchor = xform.xform(snap_point(previous_anchor + (drag_to - drag_from), SNAP_GRID | SNAP_OTHER_NODES, control, SNAP_NODE_PARENT | SNAP_NODE_SIDES | SNAP_NODE_CENTER));
+ new_anchor = _position_to_anchor(control, new_anchor).snapped(Vector2(0.001, 0.001));
- _append_canvas_item(E->get());
- }
- }
+ bool use_single_axis = m->get_shift();
+ Vector2 drag_vector = xform.xform(drag_to) - xform.xform(drag_from);
+ bool use_y = Math::abs(drag_vector.y) > Math::abs(drag_vector.x);
- box_selecting = false;
- viewport->update();
+ switch (drag_type) {
+ case DRAG_ANCHOR_TOP_LEFT:
+ if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false);
+ if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false);
+ break;
+ case DRAG_ANCHOR_TOP_RIGHT:
+ if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false);
+ if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, false);
+ break;
+ case DRAG_ANCHOR_BOTTOM_RIGHT:
+ if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false);
+ if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false);
+ break;
+ case DRAG_ANCHOR_BOTTOM_LEFT:
+ if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false);
+ if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false);
+ break;
+ case DRAG_ANCHOR_ALL:
+ if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true);
+ if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true);
+ if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, true);
+ if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true);
+ break;
+ default:
+ break;
}
- return;
+ return true;
+ }
+
+ // Confirms new anchor position
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
+ _commit_canvas_item_state(drag_selection, TTR("Move anchor"));
+ drag_type = DRAG_NONE;
+ return true;
}
- // -- From now we consider that the button is BUTTON_LEFT and that it is pressed --
+ // Cancel a drag
+ if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+ _restore_canvas_item_state(drag_selection);
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
+ }
+ }
+ return false;
+}
- Map<ObjectID, BoneList>::Element *Cbone = NULL; //closest
+bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseMotion> m = p_event;
- {
- bone_ik_list.clear();
- float closest_dist = 1e20;
- int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width");
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ // Drag resize handles
+ if (drag_type == DRAG_NONE) {
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ if (selection.size() == 1) {
+ CanvasItem *canvas_item = selection[0];
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
+
+ DragType dragger[] = {
+ DRAG_TOP_LEFT,
+ DRAG_TOP,
+ DRAG_TOP_RIGHT,
+ DRAG_RIGHT,
+ DRAG_BOTTOM_RIGHT,
+ DRAG_BOTTOM,
+ DRAG_BOTTOM_LEFT,
+ DRAG_LEFT
+ };
+
+ DragType resize_drag = DRAG_NONE;
+ float radius = (select_handle->get_size().width / 2) * 1.5;
- if (E->get().from == E->get().to)
- continue;
- Vector2 s[2] = {
- E->get().from,
- E->get().to
- };
+ for (int i = 0; i < 4; i++) {
+ int prev = (i + 3) % 4;
+ int next = (i + 1) % 4;
+
+ Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
+ ofs *= (select_handle->get_size().width / 2);
+ ofs += endpoints[i];
+ if (ofs.distance_to(b->get_position()) < radius)
+ resize_drag = dragger[i * 2];
+
+ ofs = (endpoints[i] + endpoints[next]) / 2;
+ ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2);
+ if (ofs.distance_to(b->get_position()) < radius)
+ resize_drag = dragger[i * 2 + 1];
+ }
- Vector2 p = Geometry::get_closest_point_to_segment_2d(b->get_position(), s);
- float d = p.distance_to(b->get_position());
- if (d < bone_width && d < closest_dist) {
- Cbone = E;
- closest_dist = d;
+ if (resize_drag != DRAG_NONE) {
+ drag_type = resize_drag;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = List<CanvasItem *>();
+ drag_selection.push_back(canvas_item);
+ _save_canvas_item_state(drag_selection);
+ return true;
+ }
}
}
+ }
+ }
- if (Cbone) {
- Node2D *b = Object::cast_to<Node2D>(ObjectDB::get_instance(Cbone->get().bone));
-
- if (b) {
-
- bool ik_found = false;
-
- bool first = true;
-
- while (b) {
+ if (drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT || drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM ||
+ drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT) {
+ // Resize the node
+ if (m.is_valid()) {
+ CanvasItem *canvas_item = drag_selection[0];
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ //Reset state
+ canvas_item->_edit_set_state(se->undo_state);
- CanvasItem *pi = b->get_parent_item();
- if (!pi)
- break;
+ bool uniform = m->get_shift();
+ bool symmetric = m->get_alt();
- float len = pi->get_global_transform().get_origin().distance_to(b->get_global_position());
- b = Object::cast_to<Node2D>(pi);
- if (!b)
- break;
+ Rect2 local_rect = canvas_item->_edit_get_rect();
+ float aspect = local_rect.get_size().y / local_rect.get_size().x;
+ Point2 current_begin = local_rect.get_position();
+ Point2 current_end = local_rect.get_position() + local_rect.get_size();
+ Point2 max_begin = (symmetric) ? (current_begin + current_end - canvas_item->_edit_get_minimum_size()) / 2.0 : current_end - canvas_item->_edit_get_minimum_size();
+ Point2 min_end = (symmetric) ? (current_begin + current_end + canvas_item->_edit_get_minimum_size()) / 2.0 : current_begin + canvas_item->_edit_get_minimum_size();
+ Point2 center = (current_begin + current_end) / 2.0;
+
+ drag_to = transform.affine_inverse().xform(m->get_position());
+
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse();
+
+ Point2 drag_to_snapped_begin = snap_point(xform.affine_inverse().xform(current_begin) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, canvas_item);
+ Point2 drag_to_snapped_end = snap_point(xform.affine_inverse().xform(current_end) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, canvas_item);
+ Point2 drag_begin = xform.xform(drag_to_snapped_begin);
+ Point2 drag_end = xform.xform(drag_to_snapped_end);
+
+ // Horizontal resize
+ if (drag_type == DRAG_LEFT || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_BOTTOM_LEFT) {
+ current_begin.x = MIN(drag_begin.x, max_begin.x);
+ } else if (drag_type == DRAG_RIGHT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_RIGHT) {
+ current_end.x = MAX(drag_end.x, min_end.x);
+ }
- if (first) {
+ // Vertical resize
+ if (drag_type == DRAG_TOP || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT) {
+ current_begin.y = MIN(drag_begin.y, max_begin.y);
+ } else if (drag_type == DRAG_BOTTOM || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT) {
+ current_end.y = MAX(drag_end.y, min_end.y);
+ }
- bone_orig_xform = b->get_global_transform();
- first = false;
+ // Uniform resize
+ if (uniform) {
+ if (drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT) {
+ current_end.y = current_begin.y + aspect * (current_end.x - current_begin.x);
+ } else if (drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM) {
+ current_end.x = current_begin.x + (current_end.y - current_begin.y) / aspect;
+ } else {
+ if (aspect >= 1.0) {
+ if (drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT) {
+ current_begin.y = current_end.y - aspect * (current_end.x - current_begin.x);
+ } else {
+ current_end.y = current_begin.y + aspect * (current_end.x - current_begin.x);
}
-
- BoneIK bik;
- bik.node = b;
- bik.len = len;
- bik.orig_state = b->_edit_get_state();
-
- bone_ik_list.push_back(bik);
-
- if (b->has_meta("_edit_ik_")) {
-
- ik_found = bone_ik_list.size() > 1;
- break;
+ } else {
+ if (drag_type == DRAG_TOP_LEFT || drag_type == DRAG_BOTTOM_LEFT) {
+ current_begin.x = current_end.x - (current_end.y - current_begin.y) / aspect;
+ } else {
+ current_end.x = current_begin.x + (current_end.y - current_begin.y) / aspect;
}
-
- if (!pi->has_meta("_edit_bone_"))
- break;
}
-
- if (!ik_found)
- bone_ik_list.clear();
}
}
- }
-
- // Single selected item
- CanvasItem *canvas_item = _get_single_item();
- if (canvas_item) {
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- ERR_FAIL_COND(!se);
-
- Point2 click = b->get_position();
-
- // Rotation
- if ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) {
- drag = DRAG_ROTATE;
- drag_from = transform.affine_inverse().xform(click);
- se->undo_state = canvas_item->_edit_get_state();
- if (Object::cast_to<Node2D>(canvas_item))
- se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot();
- if (Object::cast_to<Control>(canvas_item))
- se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
- se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->_edit_get_rect();
- return;
- }
- if (tool == TOOL_SELECT) {
- // Open a sub-scene on double-click
- if (b->is_doubleclick()) {
- if (canvas_item->get_filename() != "" && canvas_item != editor->get_edited_scene()) {
- editor->open_request(canvas_item->get_filename());
- return;
- }
- }
-
- // Drag resize handles
- drag = _get_resize_handle_drag_type(click, drag_point_from);
- if (drag != DRAG_NONE) {
- drag_from = transform.affine_inverse().xform(click);
- se->undo_state = canvas_item->_edit_get_state();
- if (Object::cast_to<Node2D>(canvas_item))
- se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot();
- if (Object::cast_to<Control>(canvas_item))
- se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
- se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->_edit_get_rect();
- return;
+ // Symmetric resize
+ if (symmetric) {
+ if (drag_type == DRAG_LEFT || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_BOTTOM_LEFT) {
+ current_end.x = 2.0 * center.x - current_begin.x;
+ } else if (drag_type == DRAG_RIGHT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_RIGHT) {
+ current_begin.x = 2.0 * center.x - current_end.x;
}
-
- // Drag anchor handles
- Control *control = Object::cast_to<Control>(canvas_item);
- if (control && show_helpers && !Object::cast_to<Container>(control->get_parent())) {
- drag = _get_anchor_handle_drag_type(click, drag_point_from);
- if (drag != DRAG_NONE) {
- drag_from = transform.affine_inverse().xform(click);
- se->undo_state = canvas_item->_edit_get_state();
- se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->_edit_get_rect();
- return;
- }
+ if (drag_type == DRAG_TOP || drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT) {
+ current_end.y = 2.0 * center.y - current_begin.y;
+ } else if (drag_type == DRAG_BOTTOM || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT) {
+ current_begin.y = 2.0 * center.y - current_end.y;
}
}
+ canvas_item->_edit_set_rect(Rect2(current_begin, current_end - current_begin));
+ return true;
}
- // Multiple selected items
- Point2 click = b->get_position();
+ // Confirm resize
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) {
+ _commit_canvas_item_state(drag_selection, TTR("Resize CanvasItem"));
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
+ }
- if ((b->get_alt() || tool == TOOL_MOVE) && get_item_count()) {
- // Drag the nodes
- _prepare_drag(click);
+ // Cancel a drag
+ if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+ _restore_canvas_item_state(drag_selection);
+ drag_type = DRAG_NONE;
viewport->update();
- return;
+ return true;
}
+ }
+ return false;
+}
+
+bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseMotion> m = p_event;
+ Ref<InputEventKey> k = p_event;
- CanvasItem *c = NULL;
- if (Cbone) {
- c = Object::cast_to<CanvasItem>(ObjectDB::get_instance(Cbone->get().bone));
- if (c)
- c = c->get_parent_item();
+ if (drag_type == DRAG_NONE) {
+ //Start moving the nodes
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ if ((b->get_alt() || tool == TOOL_MOVE) && selection.size() > 0) {
+ drag_type = DRAG_ALL;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = selection;
+ _save_canvas_item_state(drag_selection);
+ return true;
+ }
}
+ }
- Node *scene = editor->get_edited_scene();
- if (!scene)
- return;
- // Find the item to select
- if (!c) {
- Vector<_SelectResult> selection;
- _find_canvas_items_at_pos(click, scene, transform, Transform2D(), selection, 1);
- if (!selection.empty())
- c = selection[0].item;
+ if (drag_type == DRAG_ALL) {
+ // Move the nodes
+ if (m.is_valid()) {
+ _restore_canvas_item_state(drag_selection, true);
+
+ drag_to = transform.affine_inverse().xform(m->get_position());
+ Point2 previous_pos;
+ if (drag_selection.size() == 1) {
+ Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
+ previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
+ } else {
+ previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
+ }
+ Point2 new_pos = snap_point(previous_pos + (drag_to - drag_from), SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES);
+ bool single_axis = m->get_shift();
+ if (single_axis) {
+ if (ABS(new_pos.x - previous_pos.x) > ABS(new_pos.y - previous_pos.y)) {
+ new_pos.y = previous_pos.y;
+ } else {
+ new_pos.x = previous_pos.x;
+ }
+ }
+
+ bool force_no_IK = m->get_alt();
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
- CanvasItem *cn = c;
- while (cn) {
- if (cn->has_meta("_edit_group_")) {
- c = cn;
+ Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
+ if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) {
+ _solve_IK(node2d, new_pos);
+ } else {
+ canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
}
- cn = cn->get_parent_item();
}
+ return true;
}
- Node *n = c;
- while ((n && n != scene && n->get_owner() != scene) || (n && !n->is_class("CanvasItem"))) {
- n = n->get_parent();
- };
+ // Confirm the move (only if it was moved)
+ if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT && (drag_type == DRAG_ALL)) {
+ if (transform.affine_inverse().xform(b->get_position()) != drag_from) {
+ _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true);
+ }
- if (n) {
- c = Object::cast_to<CanvasItem>(n);
- } else {
- c = NULL;
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
}
- // Select the item
- additive_selection = b->get_shift();
- if (!c) {
- _select_click_on_empty_area(click, additive_selection, true);
- } else if (!_select_click_on_item(c, click, additive_selection, true)) {
- return;
+ // Cancel a drag
+ if (b.is_valid() && b->get_button_index() == BUTTON_RIGHT && b->is_pressed()) {
+ _restore_canvas_item_state(drag_selection, true);
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
}
}
- Ref<InputEventMouseMotion> m = p_event;
- if (m.is_valid()) {
- // Mouse motion event
- _update_cursor();
-
- if (box_selecting) {
- // Update box selection
- box_selecting_to = transform.affine_inverse().xform(m->get_position());
- viewport->update();
- return;
+ // Move the canvas items with the arrow keys
+ if (k.is_valid() && k->is_pressed() && tool == TOOL_SELECT &&
+ (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) {
+ if (!k->is_echo()) {
+ // Start moving the canvas items with the keyboard
+ drag_selection = _get_edited_canvas_items();
+ drag_type = DRAG_KEY_MOVE;
+ drag_from = Vector2();
+ drag_to = Vector2();
+ _save_canvas_item_state(drag_selection, true);
}
- if (drag == DRAG_NONE) {
- bool space_pressed = Input::get_singleton()->is_key_pressed(KEY_SPACE);
- bool simple_panning = EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning");
- int button = m->get_button_mask();
+ if (drag_selection.size() > 0) {
- // Check if any of the panning triggers are activated
- bool panning_tool = (button & BUTTON_MASK_LEFT) && tool == TOOL_PAN;
- bool panning_middle_button = button & BUTTON_MASK_MIDDLE;
- bool panning_spacebar = (button & BUTTON_MASK_LEFT) && space_pressed;
- bool panning_spacebar_simple = space_pressed && simple_panning;
+ _restore_canvas_item_state(drag_selection, true);
- if (panning_tool || panning_middle_button || panning_spacebar || panning_spacebar_simple) {
- // Pan the viewport
- Point2i relative;
- if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) {
- relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect());
+ bool move_local_base = k->get_alt();
+ bool move_local_base_rotated = k->get_control() || k->get_metakey();
+
+ Vector2 dir;
+ if (k->get_scancode() == KEY_UP)
+ dir += Vector2(0, -1);
+ else if (k->get_scancode() == KEY_DOWN)
+ dir += Vector2(0, 1);
+ else if (k->get_scancode() == KEY_LEFT)
+ dir += Vector2(-1, 0);
+ else if (k->get_scancode() == KEY_RIGHT)
+ dir += Vector2(1, 0);
+ if (k->get_shift())
+ dir *= grid_step * Math::pow(2.0, grid_step_multiplier);
+
+ drag_to += dir;
+ if (k->get_shift())
+ drag_to = drag_to.snapped(grid_step * Math::pow(2.0, grid_step_multiplier));
+
+ Point2 previous_pos;
+ if (drag_selection.size() == 1) {
+ Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
+ previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
+ } else {
+ previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
+ }
+
+ Point2 new_pos;
+ if (drag_selection.size() == 1) {
+ Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]);
+ if (node_2d && move_local_base_rotated) {
+ Transform2D m;
+ m.rotate(node_2d->get_rotation());
+ new_pos += m.xform(drag_to);
+ } else if (move_local_base) {
+ new_pos += drag_to;
} else {
- relative = m->get_relative();
+ new_pos = previous_pos + (drag_to - drag_from);
}
+ } else {
+ new_pos = previous_pos + (drag_to - drag_from);
+ }
- h_scroll->set_value(h_scroll->get_value() - relative.x / zoom);
- v_scroll->set_value(v_scroll->get_value() - relative.y / zoom);
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
+
+ Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
+ if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
+ _solve_IK(node2d, new_pos);
+ } else {
+ canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ }
}
+ }
+ return true;
+ }
- return;
+ if (k.is_valid() && !k->is_pressed() && drag_type == DRAG_KEY_MOVE && tool == TOOL_SELECT &&
+ (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) {
+ // Confirm canvas items move by arrow keys
+ if ((!Input::get_singleton()->is_key_pressed(KEY_UP)) &&
+ (!Input::get_singleton()->is_key_pressed(KEY_DOWN)) &&
+ (!Input::get_singleton()->is_key_pressed(KEY_LEFT)) &&
+ (!Input::get_singleton()->is_key_pressed(KEY_RIGHT))) {
+ _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true);
+ drag_type = DRAG_NONE;
}
+ viewport->update();
+ return true;
+ }
- List<Node *> selection = editor_selection->get_selected_node_list();
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ if (k.is_valid() && (k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) {
+ // Accept the key event in any case
+ return true;
+ }
+ return false;
+}
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
+bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
+ Ref<InputEventMouseButton> b = p_event;
+ Ref<InputEventMouseMotion> m = p_event;
+ Ref<InputEventKey> k = p_event;
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
+ if (drag_type == DRAG_NONE) {
+ if (b.is_valid() &&
+ ((b->get_button_index() == BUTTON_RIGHT && b->get_alt() && tool == TOOL_SELECT) ||
+ (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT))) {
+ // Popup the selection menu list
+ Point2 click = transform.affine_inverse().xform(b->get_position());
- bool dragging_bone = drag == DRAG_ALL && selection.size() == 1 && bone_ik_list.size();
+ _get_canvas_items_at_pos(click, selection_results);
- if (!dragging_bone) {
- canvas_item->_edit_set_state(se->undo_state); //reset state and reapply
- if (Object::cast_to<Node2D>(canvas_item))
- Object::cast_to<Node2D>(canvas_item)->_edit_set_pivot(se->undo_pivot);
- if (Object::cast_to<Control>(canvas_item))
- Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot);
- }
+ if (selection_results.size() == 1) {
+ CanvasItem *item = selection_results[0].item;
+ selection_results.clear();
- Vector2 dfrom = drag_from;
- Vector2 dto = transform.affine_inverse().xform(m->get_position());
- if (canvas_item->has_meta("_edit_lock_"))
- continue;
+ _select_click_on_item(item, click, b->get_shift());
- if (drag == DRAG_ROTATE) {
- // Rotate the node
- Vector2 center = canvas_item->get_global_transform_with_canvas().get_origin();
- {
- Node2D *node = Object::cast_to<Node2D>(canvas_item);
-
- if (node) {
- real_t angle = node->get_rotation();
- node->set_rotation(snap_angle(angle + (dfrom - center).angle_to(dto - center), angle));
- display_rotate_to = dto;
- display_rotate_from = center;
- viewport->update();
- }
- }
+ return true;
+ } else if (!selection_results.empty()) {
+ // Sorts items according the their z-index
+ selection_results.sort();
- {
- Control *node = Object::cast_to<Control>(canvas_item);
+ NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
+ StringName root_name = root_path.get_name(root_path.get_name_count() - 1);
- if (node) {
- real_t angle = node->get_rotation();
- node->set_rotation(snap_angle(angle + (dfrom - center).angle_to(dto - center), angle));
- display_rotate_to = dto;
- display_rotate_from = center;
- viewport->update();
- }
- }
+ for (int i = 0; i < selection_results.size(); i++) {
+ CanvasItem *item = selection_results[i].item;
- continue;
- }
+ Ref<Texture> icon;
+ if (item->has_meta("_editor_icon"))
+ icon = item->get_meta("_editor_icon");
+ else
+ icon = get_icon(has_icon(item->get_class(), "EditorIcons") ? item->get_class() : String("Object"), "EditorIcons");
+ String node_path = "/" + root_name + "/" + root_path.rel_path_to(item->get_path());
- bool uniform = m->get_shift();
- bool symmetric = m->get_alt();
+ selection_menu->add_item(item->get_name());
+ selection_menu->set_item_icon(i, icon);
+ selection_menu->set_item_metadata(i, node_path);
+ selection_menu->set_item_tooltip(i, String(item->get_name()) + "\nType: " + item->get_class() + "\nPath: " + node_path);
+ }
- Vector2 drag_vector =
- canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) -
- canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom);
-
- switch (drag) {
- case DRAG_ALL:
- case DRAG_NODE_2D:
- dto -= drag_from - drag_point_from;
- if (uniform) {
- if (ABS(dto.x - drag_point_from.x) > ABS(dto.y - drag_point_from.y)) {
- dto.y = drag_point_from.y;
- } else {
- dto.x = drag_point_from.x;
- }
- }
- break;
+ selection_menu_additive_selection = b->get_shift();
+ selection_menu->set_global_position(b->get_global_position());
+ selection_menu->popup();
+ return true;
}
+ }
- Control *control = Object::cast_to<Control>(canvas_item);
- if (control) {
- // Drag and snap the anchor
- Transform2D c_trans_rev = canvas_item->get_global_transform_with_canvas().affine_inverse();
+ if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
+ // Single item selection
+ Point2 click = transform.affine_inverse().xform(b->get_position());
- Vector2 anchor = c_trans_rev.xform(dto - drag_from + drag_point_from);
- anchor = _position_to_anchor(control, anchor);
+ Node *scene = editor->get_edited_scene();
+ if (!scene)
+ return true;
- Vector2 anchor_snapped = c_trans_rev.xform(snap_point(dto - drag_from + drag_point_from, SNAP_GRID | SNAP_GUIDES | SNAP_OTHER_NODES, _get_single_item(), SNAP_NODE_PARENT | SNAP_NODE_SIDES));
- anchor_snapped = _position_to_anchor(control, anchor_snapped).snapped(Vector2(0.00001, 0.00001));
+ // Find the item to select
+ CanvasItem *canvas_item = NULL;
+ Vector<_SelectResult> selection;
+ _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0);
- bool use_y = Math::abs(drag_vector.y) > Math::abs(drag_vector.x);
+ for (int i = 0; i < selection.size(); i++) {
+ if (editor_selection->is_selected(selection[i].item)) {
+ // Drag the node(s) if requested
+ List<CanvasItem *> selection = _get_edited_canvas_items();
- switch (drag) {
- case DRAG_ANCHOR_TOP_LEFT:
- if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x, false);
- if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y, false);
- continue;
- break;
- case DRAG_ANCHOR_TOP_RIGHT:
- if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x, false);
- if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y, false);
- continue;
- break;
- case DRAG_ANCHOR_BOTTOM_RIGHT:
- if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x, false);
- if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y, false);
- break;
- case DRAG_ANCHOR_BOTTOM_LEFT:
- if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x, false);
- if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y, false);
- continue;
- break;
- case DRAG_ANCHOR_ALL:
- if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_LEFT, anchor_snapped.x, false);
- if (!uniform || (uniform && !use_y)) control->set_anchor(MARGIN_RIGHT, anchor_snapped.x, false);
- if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_TOP, anchor_snapped.y, false);
- if (!uniform || (uniform && use_y)) control->set_anchor(MARGIN_BOTTOM, anchor_snapped.y, false);
- continue;
- break;
+ drag_type = DRAG_ALL;
+ drag_selection = selection;
+ drag_from = click;
+ _save_canvas_item_state(drag_selection);
+
+ return true;
}
}
- dfrom = drag_point_from;
- dto = snap_point(dto, SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL, _get_single_item());
-
- drag_vector =
- canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) -
- canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom);
+ if (!selection.empty())
+ canvas_item = selection[0].item;
- Rect2 local_rect = canvas_item->_edit_get_rect();
- Vector2 begin = local_rect.position;
- Vector2 end = local_rect.position + local_rect.size;
- Vector2 minsize = canvas_item->_edit_get_minimum_size();
+ if (!canvas_item) {
+ // Start a box selection
+ if (!b->get_shift()) {
+ // Clear the selection if not additive
+ editor_selection->clear();
+ viewport->update();
+ };
- if (uniform) {
- // Keep the height/width ratio of the item
- float aspect = local_rect.size.aspect();
- switch (drag) {
- case DRAG_LEFT:
- drag_vector.y = -drag_vector.x / aspect;
- break;
- case DRAG_RIGHT:
- drag_vector.y = drag_vector.x / aspect;
- break;
- case DRAG_TOP:
- drag_vector.x = -drag_vector.y * aspect;
- break;
- case DRAG_BOTTOM:
- drag_vector.x = drag_vector.y * aspect;
- break;
- case DRAG_BOTTOM_LEFT:
- case DRAG_TOP_RIGHT:
- if (aspect > 1.0) { // width > height, take x as reference
- drag_vector.y = -drag_vector.x / aspect;
- } else { // height > width, take y as reference
- drag_vector.x = -drag_vector.y * aspect;
- }
- break;
- case DRAG_BOTTOM_RIGHT:
- case DRAG_TOP_LEFT:
- if (aspect > 1.0) { // width > height, take x as reference
- drag_vector.y = drag_vector.x / aspect;
- } else { // height > width, take y as reference
- drag_vector.x = drag_vector.y * aspect;
- }
- break;
- }
+ drag_from = click;
+ drag_type = DRAG_BOX_SELECTION;
+ box_selecting_to = drag_from;
+ return true;
} else {
- switch (drag) {
- case DRAG_RIGHT:
- case DRAG_LEFT:
- drag_vector.y = 0;
- break;
- case DRAG_TOP:
- case DRAG_BOTTOM:
- drag_vector.x = 0;
- break;
+ bool still_selected = _select_click_on_item(canvas_item, click, b->get_shift());
+ // Start dragging
+ if (still_selected) {
+ // Drag the node(s) if requested
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+
+ drag_type = DRAG_ALL;
+ drag_selection = selection;
+ drag_from = click;
+ _save_canvas_item_state(drag_selection);
}
+ // Select the item
+ return true;
}
+ }
+ }
- switch (drag) {
- case DRAG_ALL:
- begin += drag_vector;
- end += drag_vector;
- break;
- case DRAG_RIGHT:
- case DRAG_BOTTOM:
- case DRAG_BOTTOM_RIGHT:
- incend(begin.x, end.x, drag_vector.x, minsize.x, symmetric);
- incend(begin.y, end.y, drag_vector.y, minsize.y, symmetric);
- break;
- case DRAG_TOP_LEFT:
- incbeg(begin.x, end.x, drag_vector.x, minsize.x, symmetric);
- incbeg(begin.y, end.y, drag_vector.y, minsize.y, symmetric);
- break;
- case DRAG_TOP:
- case DRAG_TOP_RIGHT:
- incbeg(begin.y, end.y, drag_vector.y, minsize.y, symmetric);
- incend(begin.x, end.x, drag_vector.x, minsize.x, symmetric);
- break;
- case DRAG_LEFT:
- case DRAG_BOTTOM_LEFT:
- incbeg(begin.x, end.x, drag_vector.x, minsize.x, symmetric);
- incend(begin.y, end.y, drag_vector.y, minsize.y, symmetric);
- break;
-
- case DRAG_PIVOT:
+ if (drag_type == DRAG_BOX_SELECTION) {
+ if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT) {
+ // Confirms box selection
+ Node *scene = editor->get_edited_scene();
+ if (scene) {
+ List<CanvasItem *> selitems;
- if (Object::cast_to<Node2D>(canvas_item)) {
- Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
- n2d->_edit_set_pivot(se->undo_pivot + drag_vector);
- }
- if (Object::cast_to<Control>(canvas_item)) {
- Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot + drag_vector);
- }
- continue;
- break;
- case DRAG_NODE_2D:
+ Point2 bsfrom = drag_from;
+ Point2 bsto = box_selecting_to;
+ if (bsfrom.x > bsto.x)
+ SWAP(bsfrom.x, bsto.x);
+ if (bsfrom.y > bsto.y)
+ SWAP(bsfrom.y, bsto.y);
- ERR_FAIL_COND(!Object::cast_to<Node2D>(canvas_item));
- Object::cast_to<Node2D>(canvas_item)->set_global_position(dto);
- continue;
- break;
+ _find_canvas_items_in_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems);
+ for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) {
+ editor_selection->add_node(E->get());
+ }
}
- if (!dragging_bone) {
-
- local_rect.position = begin;
- local_rect.size = end - begin;
- canvas_item->_edit_set_rect(local_rect);
-
- } else {
- //ok, all that had to be done was done, now solve IK
-
- Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
- Transform2D final_xform = bone_orig_xform;
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
+ }
- if (n2d) {
+ if (b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_RIGHT) {
+ // Cancel box selection
+ drag_type = DRAG_NONE;
+ viewport->update();
+ return true;
+ }
- float total_len = 0;
- for (List<BoneIK>::Element *E = bone_ik_list.front(); E; E = E->next()) {
- if (E->prev())
- total_len += E->get().len;
- E->get().pos = E->get().node->get_global_transform().get_origin();
- }
+ if (m.is_valid()) {
+ // Update box selection
+ box_selecting_to = transform.affine_inverse().xform(m->get_position());
+ viewport->update();
+ return true;
+ }
+ }
- {
+ if (k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_ESCAPE && drag_type == DRAG_NONE && tool == TOOL_SELECT) {
+ // Unselect everything
+ editor_selection->clear();
+ viewport->update();
+ }
+ return false;
+}
- final_xform.elements[2] += dto - dfrom; //final_xform.affine_inverse().basis_xform_inv(drag_vector);
- //n2d->set_global_transform(final_xform);
- }
+bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
- CanvasItem *last = bone_ik_list.back()->get().node;
- if (!last)
+ Ref<InputEventMouseMotion> m = p_event;
+ if (m.is_valid()) {
+ if (drag_type == DRAG_NONE && tool == TOOL_SELECT) {
+ Point2 click = transform.affine_inverse().xform(m->get_position());
+
+ //Checks if the hovered items changed, update the viewport if so
+ Vector<_SelectResult> hovering_results_tmp;
+ _get_canvas_items_at_pos(click, hovering_results_tmp);
+ hovering_results_tmp.sort();
+ bool changed = false;
+ if (hovering_results.size() == hovering_results_tmp.size()) {
+ for (int i = 0; i < hovering_results.size(); i++) {
+ if (hovering_results[i].item != hovering_results_tmp[i].item) {
+ changed = true;
break;
-
- Vector2 root_pos = last->get_global_transform().get_origin();
- Vector2 leaf_pos = final_xform.get_origin();
-
- if ((leaf_pos.distance_to(root_pos)) > total_len) {
- //oops dude you went too far
- //print_line("TOO FAR!");
- Vector2 rel = leaf_pos - root_pos;
- rel = rel.normalized() * total_len;
- leaf_pos = root_pos + rel;
- }
-
- bone_ik_list.front()->get().pos = leaf_pos;
-
- //print_line("BONE IK LIST "+itos(bone_ik_list.size()));
-
- if (bone_ik_list.size() > 2) {
- int solver_iterations = 64;
- float solver_k = 0.3;
-
- for (int i = 0; i < solver_iterations; i++) {
-
- for (List<BoneIK>::Element *E = bone_ik_list.front(); E; E = E->next()) {
-
- if (E == bone_ik_list.back()) {
-
- break;
- }
-
- float len = E->next()->get().len;
-
- if (E->next() == bone_ik_list.back()) {
-
- //print_line("back");
-
- Vector2 rel = E->get().pos - E->next()->get().pos;
- //print_line("PREV "+E->get().pos);
- Vector2 desired = E->next()->get().pos + rel.normalized() * len;
- //print_line("DESIRED "+desired);
- E->get().pos = E->get().pos.linear_interpolate(desired, solver_k);
- //print_line("POST "+E->get().pos);
-
- } else if (E == bone_ik_list.front()) {
- //only adjust parent
- //print_line("front");
- Vector2 rel = E->next()->get().pos - E->get().pos;
- //print_line("PREV "+E->next()->get().pos);
- Vector2 desired = E->get().pos + rel.normalized() * len;
- //print_line("DESIRED "+desired);
- E->next()->get().pos = E->next()->get().pos.linear_interpolate(desired, solver_k);
- //print_line("POST "+E->next()->get().pos);
- } else {
-
- Vector2 rel = E->next()->get().pos - E->get().pos;
- Vector2 cen = (E->next()->get().pos + E->get().pos) * 0.5;
- rel = rel.linear_interpolate(rel.normalized() * len, solver_k);
- rel *= 0.5;
- E->next()->get().pos = cen + rel;
- E->get().pos = cen - rel;
- //print_line("mid");
- }
- }
- }
}
}
+ } else {
+ changed = true;
+ }
- for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) {
-
- Node2D *n = E->get().node;
-
- if (!E->prev()) {
- //last goes to what it was
- final_xform.set_origin(n->get_global_position());
- n->set_global_transform(final_xform);
+ if (changed) {
+ hovering_results = hovering_results_tmp;
+ viewport->update();
+ }
- } else {
- Vector2 rel = (E->prev()->get().node->get_global_position() - n->get_global_position()).normalized();
- Vector2 rel2 = (E->prev()->get().pos - E->get().pos).normalized();
- float rot = rel.angle_to(rel2);
- if (n->get_global_transform().basis_determinant() < 0) {
- //mirrored, rotate the other way
- rot = -rot;
- }
+ return true;
+ }
+ }
- n->rotate(rot);
- }
- }
+ return false;
+}
- break;
+void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
+ bool accepted = false;
+ if ((accepted = _gui_input_rulers_and_guides(p_event))) {
+ //printf("Rulers and guides\n");
+ } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) {
+ //printf("Plugin\n");
+ } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) {
+ //printf("Open scene on double click\n");
+ } else if ((accepted = _gui_input_anchors(p_event))) {
+ //printf("Anchors\n");
+ } else if ((accepted = _gui_input_pivot(p_event))) {
+ //printf("Set pivot\n");
+ } else if ((accepted = _gui_input_resize(p_event))) {
+ //printf("Resize\n");
+ } else if ((accepted = _gui_input_rotate(p_event))) {
+ //printf("Rotate\n");
+ } else if ((accepted = _gui_input_move(p_event))) {
+ //printf("Move\n");
+ } else if ((accepted = _gui_input_zoom_or_pan(p_event))) {
+ //printf("Zoom or pan\n");
+ } else if ((accepted = _gui_input_select(p_event))) {
+ //printf("Selection\n");
+ }
+
+ if (accepted)
+ accept_event();
+
+ // Handles the mouse hovering
+ _gui_input_hover(p_event);
+
+ // Change the cursor
+ CursorShape c = CURSOR_ARROW;
+ switch (drag_type) {
+ case DRAG_NONE:
+ switch (tool) {
+ case TOOL_MOVE:
+ c = CURSOR_MOVE;
+ break;
+ case TOOL_EDIT_PIVOT:
+ c = CURSOR_CROSS;
+ break;
+ case TOOL_PAN:
+ c = CURSOR_DRAG;
+ break;
+ default:
+ break;
}
- }
+ break;
+ case DRAG_LEFT:
+ case DRAG_RIGHT:
+ c = CURSOR_HSIZE;
+ break;
+ case DRAG_TOP:
+ case DRAG_BOTTOM:
+ c = CURSOR_VSIZE;
+ break;
+ case DRAG_TOP_LEFT:
+ case DRAG_BOTTOM_RIGHT:
+ c = CURSOR_FDIAGSIZE;
+ break;
+ case DRAG_TOP_RIGHT:
+ case DRAG_BOTTOM_LEFT:
+ c = CURSOR_BDIAGSIZE;
+ break;
+ case DRAG_ALL:
+ c = CURSOR_MOVE;
+ break;
+ case DRAG_PAN:
+ c = CURSOR_DRAG;
+ default:
+ break;
+ }
+ viewport->set_default_cursor_shape(c);
+
+ // Grab focus
+ if (!viewport->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) {
+ viewport->call_deferred("grab_focus");
}
}
@@ -2292,8 +2037,8 @@ void CanvasItemEditor::_draw_percentage_at_position(float p_value, Point2 p_posi
void CanvasItemEditor::_draw_focus() {
// Draw the focus around the base viewport
- if (viewport_base->has_focus()) {
- get_stylebox("Focus", "EditorStyles")->draw(viewport_base->get_canvas_item(), Rect2(Point2(), viewport_base->get_size()));
+ if (viewport->has_focus()) {
+ get_stylebox("Focus", "EditorStyles")->draw(viewport->get_canvas_item(), Rect2(Point2(), viewport->get_size()));
}
}
@@ -2306,63 +2051,62 @@ void CanvasItemEditor::_draw_guides() {
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) {
Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_");
for (int i = 0; i < vguides.size(); i++) {
- if (drag == DRAG_V_GUIDE && i == edited_guide_index)
+ if (drag_type == DRAG_V_GUIDE && i == dragged_guide_index)
continue;
float x = xform.xform(Point2(vguides[i], 0)).x;
- viewport_base->draw_line(Point2(x, 0), Point2(x, viewport_base->get_size().y), guide_color);
+ viewport->draw_line(Point2(x, 0), Point2(x, viewport->get_size().y), guide_color);
}
}
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) {
Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_");
for (int i = 0; i < hguides.size(); i++) {
- if (drag == DRAG_H_GUIDE && i == edited_guide_index)
+ if (drag_type == DRAG_H_GUIDE && i == dragged_guide_index)
continue;
float y = xform.xform(Point2(0, hguides[i])).y;
- viewport_base->draw_line(Point2(0, y), Point2(viewport_base->get_size().x, y), guide_color);
+ viewport->draw_line(Point2(0, y), Point2(viewport->get_size().x, y), guide_color);
}
}
// Dragged guide
Color text_color = get_color("font_color", "Editor");
text_color.a = 0.5;
- if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_V_GUIDE) {
- String str = vformat("%d px", xform.affine_inverse().xform(edited_guide_pos).x);
+ if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_V_GUIDE) {
+ String str = vformat("%d px", xform.affine_inverse().xform(dragged_guide_pos).x);
Ref<Font> font = get_font("font", "Label");
Size2 text_size = font->get_string_size(str);
- viewport_base->draw_string(font, Point2(edited_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, text_color);
- viewport_base->draw_line(Point2(edited_guide_pos.x, 0), Point2(edited_guide_pos.x, viewport_base->get_size().y), guide_color);
+ viewport->draw_string(font, Point2(dragged_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, text_color);
+ viewport->draw_line(Point2(dragged_guide_pos.x, 0), Point2(dragged_guide_pos.x, viewport->get_size().y), guide_color);
}
- if (drag == DRAG_DOUBLE_GUIDE || drag == DRAG_H_GUIDE) {
- String str = vformat("%d px", xform.affine_inverse().xform(edited_guide_pos).y);
+ if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_H_GUIDE) {
+ String str = vformat("%d px", xform.affine_inverse().xform(dragged_guide_pos).y);
Ref<Font> font = get_font("font", "Label");
Size2 text_size = font->get_string_size(str);
- viewport_base->draw_string(font, Point2(RULER_WIDTH + 10, edited_guide_pos.y + text_size.y / 2 + 10), str, text_color);
- viewport_base->draw_line(Point2(0, edited_guide_pos.y), Point2(viewport_base->get_size().x, edited_guide_pos.y), guide_color);
+ viewport->draw_string(font, Point2(RULER_WIDTH + 10, dragged_guide_pos.y + text_size.y / 2 + 10), str, text_color);
+ viewport->draw_line(Point2(0, dragged_guide_pos.y), Point2(viewport->get_size().x, dragged_guide_pos.y), guide_color);
}
}
void CanvasItemEditor::_draw_rulers() {
- Color graduation_color = get_color("font_color", "Editor");
- graduation_color.a = 0.5;
Color bg_color = get_color("dark_color_2", "Editor");
+ Color graduation_color = get_color("font_color", "Editor").linear_interpolate(bg_color, 0.5);
Color font_color = get_color("font_color", "Editor");
font_color.a = 0.8;
Ref<Font> font = get_font("rulers", "EditorFonts");
+ bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL);
// The rule transform
- Transform2D ruler_transform;
- if (show_grid || snap_grid) {
- ruler_transform = Transform2D();
- if (snap_relative && get_item_count() > 0) {
- ruler_transform.translate(_find_topleftmost_point());
+ Transform2D ruler_transform = Transform2D();
+ if (show_grid || (is_snap_active && snap_grid)) {
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ if (snap_relative && selection.size() > 0) {
+ ruler_transform.translate(_get_encompassing_rect_from_list(selection).position);
ruler_transform.scale_basis(grid_step * Math::pow(2.0, grid_step_multiplier));
} else {
ruler_transform.translate(grid_offset);
ruler_transform.scale_basis(grid_step * Math::pow(2.0, grid_step_multiplier));
}
while ((transform * ruler_transform).get_scale().x < 50 || (transform * ruler_transform).get_scale().y < 50) {
-
ruler_transform.scale_basis(Point2(2, 2));
}
} else {
@@ -2373,7 +2117,6 @@ void CanvasItemEditor::_draw_rulers() {
for (int i = 0; basic_rule * zoom < 100; i++) {
basic_rule *= (i % 2) ? 2.0 : 5.0;
}
- ruler_transform = Transform2D();
ruler_transform.scale(Size2(basic_rule, basic_rule));
}
@@ -2387,43 +2130,43 @@ void CanvasItemEditor::_draw_rulers() {
minor_subdivide.scale(Size2(1.0 / minor_subdivision, 1.0 / minor_subdivision));
// First and last graduations to draw (in the ruler space)
- Point2 first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Point2());
+ Point2 first = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(Point2(RULER_WIDTH, RULER_WIDTH));
Point2 last = (transform * ruler_transform * major_subdivide * minor_subdivide).affine_inverse().xform(viewport->get_size());
// Draw top ruler
- viewport_base->draw_rect(Rect2(Point2(RULER_WIDTH, 0), Size2(viewport->get_size().x, RULER_WIDTH)), bg_color);
+ viewport->draw_rect(Rect2(Point2(RULER_WIDTH, 0), Size2(viewport->get_size().x, RULER_WIDTH)), bg_color);
for (int i = Math::ceil(first.x); i < last.x; i++) {
Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0));
if (i % (major_subdivision * minor_subdivision) == 0) {
- viewport_base->draw_line(Point2(position.x + RULER_WIDTH, 0), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color);
+ viewport->draw_line(Point2(position.x, 0), Point2(position.x, RULER_WIDTH), graduation_color);
float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x;
- viewport_base->draw_string(font, Point2(position.x + RULER_WIDTH + 2, font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color);
+ viewport->draw_string(font, Point2(position.x + 2, font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color);
} else {
if (i % minor_subdivision == 0) {
- viewport_base->draw_line(Point2(position.x + RULER_WIDTH, RULER_WIDTH * 0.33), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color);
+ viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.33), Point2(position.x, RULER_WIDTH), graduation_color);
} else {
- viewport_base->draw_line(Point2(position.x + RULER_WIDTH, RULER_WIDTH * 0.66), Point2(position.x + RULER_WIDTH, RULER_WIDTH), graduation_color);
+ viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.66), Point2(position.x, RULER_WIDTH), graduation_color);
}
}
}
// Draw left ruler
- viewport_base->draw_rect(Rect2(Point2(0, RULER_WIDTH), Size2(RULER_WIDTH, viewport->get_size().y)), bg_color);
+ viewport->draw_rect(Rect2(Point2(0, RULER_WIDTH), Size2(RULER_WIDTH, viewport->get_size().y)), bg_color);
for (int i = Math::ceil(first.y); i < last.y; i++) {
Point2 position = (transform * ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i));
if (i % (major_subdivision * minor_subdivision) == 0) {
- viewport_base->draw_line(Point2(0, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color);
+ viewport->draw_line(Point2(0, position.y), Point2(RULER_WIDTH, position.y), graduation_color);
float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y;
- viewport_base->draw_string(font, Point2(2, position.y + RULER_WIDTH + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color);
+ viewport->draw_string(font, Point2(2, position.y + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color);
} else {
if (i % minor_subdivision == 0) {
- viewport_base->draw_line(Point2(RULER_WIDTH * 0.33, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color);
+ viewport->draw_line(Point2(RULER_WIDTH * 0.33, position.y), Point2(RULER_WIDTH, position.y), graduation_color);
} else {
- viewport_base->draw_line(Point2(RULER_WIDTH * 0.66, position.y + RULER_WIDTH), Point2(RULER_WIDTH, position.y + RULER_WIDTH), graduation_color);
+ viewport->draw_line(Point2(RULER_WIDTH * 0.66, position.y), Point2(RULER_WIDTH, position.y), graduation_color);
}
}
}
- viewport_base->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color);
+ viewport->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color);
}
void CanvasItemEditor::_draw_grid() {
@@ -2434,8 +2177,9 @@ void CanvasItemEditor::_draw_grid() {
Transform2D xform = transform.affine_inverse();
Vector2 real_grid_offset;
- if (snap_relative && get_item_count() > 0) {
- Vector2 topleft = _find_topleftmost_point();
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ if (snap_relative && selection.size() > 0) {
+ Vector2 topleft = _get_encompassing_rect_from_list(selection).position;
real_grid_offset.x = fmod(topleft.x, grid_step.x * (real_t)Math::pow(2.0, grid_step_multiplier));
real_grid_offset.y = fmod(topleft.y, grid_step.y * (real_t)Math::pow(2.0, grid_step_multiplier));
} else {
@@ -2468,81 +2212,77 @@ void CanvasItemEditor::_draw_grid() {
}
void CanvasItemEditor::_draw_selection() {
- bool pivot_found = false;
Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons");
- bool single = _get_single_item() != NULL;
+ Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons");
+ Ref<Texture> previous_position_icon = get_icon("EditorPositionPrevious", "EditorIcons");
+
RID ci = viewport->get_canvas_item();
- Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
+ List<CanvasItem *> selection = _get_edited_canvas_items(false, false);
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
+ bool single = selection.size() == 1;
+ for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
-
- Rect2 rect = canvas_item->_edit_get_rect();
- if (show_helpers && drag != DRAG_NONE && drag != DRAG_PIVOT) {
+ // Draw the previous position if we are dragging the node
+ if (show_helpers &&
+ (drag_type == DRAG_ALL || drag_type == DRAG_ROTATE ||
+ drag_type == DRAG_LEFT || drag_type == DRAG_RIGHT || drag_type == DRAG_TOP || drag_type == DRAG_BOTTOM ||
+ drag_type == DRAG_TOP_LEFT || drag_type == DRAG_TOP_RIGHT || drag_type == DRAG_BOTTOM_LEFT || drag_type == DRAG_BOTTOM_RIGHT)) {
const Transform2D pre_drag_xform = transform * se->pre_drag_xform;
const Color pre_drag_color = Color(0.4, 0.6, 1, 0.7);
- Vector2 pre_drag_endpoints[4] = {
+ if (canvas_item->_edit_use_rect()) {
+ Vector2 pre_drag_endpoints[4] = {
- pre_drag_xform.xform(se->pre_drag_rect.position),
- pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)),
- pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size),
- pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y))
- };
+ pre_drag_xform.xform(se->pre_drag_rect.position),
+ pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)),
+ pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size),
+ pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y))
+ };
- for (int i = 0; i < 4; i++) {
- viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2);
+ for (int i = 0; i < 4; i++) {
+ viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2);
+ }
+ } else {
+ viewport->draw_texture(previous_position_icon, (pre_drag_xform.xform(Point2()) - (previous_position_icon->get_size() / 2)).floor());
}
}
Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform);
-
- Vector2 endpoints[4] = {
- xform.xform(rect.position),
- xform.xform(rect.position + Vector2(rect.size.x, 0)),
- xform.xform(rect.position + rect.size),
- xform.xform(rect.position + Vector2(0, rect.size.y))
- };
+ // Draw the selected items position / surrounding boxes
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
- Color c = Color(1, 0.6, 0.4, 0.7);
+ Color c = Color(1, 0.6, 0.4, 0.7);
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D());
+ for (int i = 0; i < 4; i++) {
+ viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2);
+ }
+ } else {
- for (int i = 0; i < 4; i++) {
- viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2);
+ Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
+ viewport->draw_set_transform_matrix(transform);
+ viewport->draw_texture(position_icon, -(position_icon->get_size() / 2));
+ viewport->draw_set_transform_matrix(Transform2D());
}
if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
-
- Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
- if (node2d) {
- if (node2d->_edit_use_pivot()) {
- viewport->draw_texture(pivot_icon, xform.get_origin() + (-pivot_icon->get_size() / 2).floor());
- can_move_pivot = true;
- pivot_found = true;
- }
+ // Draw the pivot
+ if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/
+ viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor());
}
Control *control = Object::cast_to<Control>(canvas_item);
if (control) {
- Vector2 pivot_ofs = control->get_pivot_offset();
- if (pivot_ofs != Vector2()) {
- viewport->draw_texture(pivot_icon, xform.xform(pivot_ofs) + (-pivot_icon->get_size() / 2).floor());
- }
- can_move_pivot = true;
- pivot_found = true;
-
if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) {
// Draw the helpers
Color color_base = Color(0.8, 0.8, 0.8, 0.5);
@@ -2561,10 +2301,9 @@ void CanvasItemEditor::_draw_selection() {
anchors_pos[i] = xform.xform(_anchor_to_position(control, anchors[i]));
}
- Map<Node *, Object *> &selection = editor_selection->get_selection();
// Get which anchor is dragged
int dragged_anchor = -1;
- switch (drag) {
+ switch (drag_type) {
case DRAG_ANCHOR_ALL:
case DRAG_ANCHOR_TOP_LEFT:
dragged_anchor = 0;
@@ -2578,6 +2317,8 @@ void CanvasItemEditor::_draw_selection() {
case DRAG_ANCHOR_BOTTOM_LEFT:
dragged_anchor = 3;
break;
+ default:
+ break;
}
if (dragged_anchor >= 0) {
@@ -2639,56 +2380,65 @@ void CanvasItemEditor::_draw_selection() {
node_pos_in_parent[2] = control->get_anchor(MARGIN_RIGHT) * control->get_parent_area_size().width + control->get_margin(MARGIN_RIGHT);
node_pos_in_parent[3] = control->get_anchor(MARGIN_BOTTOM) * control->get_parent_area_size().height + control->get_margin(MARGIN_BOTTOM);
- switch (drag) {
+ Point2 start, end;
+ switch (drag_type) {
case DRAG_LEFT:
case DRAG_TOP_LEFT:
case DRAG_BOTTOM_LEFT:
_draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM);
case DRAG_ALL:
- Point2 start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio));
- Point2 end = start - Vector2(control->get_margin(MARGIN_LEFT), 0);
+ start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio));
+ end = start - Vector2(control->get_margin(MARGIN_LEFT), 0);
_draw_margin_at_position(control->get_margin(MARGIN_LEFT), parent_transform.xform((start + end) / 2), MARGIN_TOP);
viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
break;
+ default:
+ break;
}
- switch (drag) {
+ switch (drag_type) {
case DRAG_RIGHT:
case DRAG_TOP_RIGHT:
case DRAG_BOTTOM_RIGHT:
_draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM);
case DRAG_ALL:
- Point2 start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio));
- Point2 end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0);
+ start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio));
+ end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0);
_draw_margin_at_position(control->get_margin(MARGIN_RIGHT), parent_transform.xform((start + end) / 2), MARGIN_BOTTOM);
viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
break;
+ default:
+ break;
}
- switch (drag) {
+ switch (drag_type) {
case DRAG_TOP:
case DRAG_TOP_LEFT:
case DRAG_TOP_RIGHT:
_draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT);
case DRAG_ALL:
- Point2 start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]);
- Point2 end = start - Vector2(0, control->get_margin(MARGIN_TOP));
+ start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]);
+ end = start - Vector2(0, control->get_margin(MARGIN_TOP));
_draw_margin_at_position(control->get_margin(MARGIN_TOP), parent_transform.xform((start + end) / 2), MARGIN_LEFT);
viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
break;
+ default:
+ break;
}
- switch (drag) {
+ switch (drag_type) {
case DRAG_BOTTOM:
case DRAG_BOTTOM_LEFT:
case DRAG_BOTTOM_RIGHT:
_draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT);
case DRAG_ALL:
- Point2 start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]);
- Point2 end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM));
+ start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]);
+ end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM));
_draw_margin_at_position(control->get_margin(MARGIN_BOTTOM), parent_transform.xform((start + end) / 2), MARGIN_RIGHT);
viewport->draw_line(parent_transform.xform(start), parent_transform.xform(end), color_base, 1);
break;
+ default:
+ break;
}
- switch (drag) {
+ switch (drag_type) {
//Draw the ghost rect if the node if rotated/scaled
case DRAG_LEFT:
case DRAG_TOP_LEFT:
@@ -2704,14 +2454,22 @@ void CanvasItemEditor::_draw_selection() {
viewport->draw_rect(parent_transform.xform(rect), color_base, false);
}
break;
+ default:
+ break;
}
}
}
- if (tool == TOOL_SELECT) {
-
+ if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
for (int i = 0; i < 4; i++) {
-
+ // Draw the resize handles
int prev = (i + 3) % 4;
int next = (i + 1) % 4;
@@ -2728,9 +2486,9 @@ void CanvasItemEditor::_draw_selection() {
}
}
}
- pivot_button->set_disabled(!pivot_found);
- if (box_selecting) {
+ if (drag_type == DRAG_BOX_SELECTION) {
+ // Draw the dragging box
Point2 bsfrom = transform.xform(drag_from);
Point2 bsto = transform.xform(box_selecting_to);
@@ -2738,33 +2496,82 @@ void CanvasItemEditor::_draw_selection() {
}
Color rotate_color(0.4, 0.7, 1.0, 0.8);
- if (drag == DRAG_ROTATE) {
- VisualServer::get_singleton()->canvas_item_add_line(ci, transform.xform(display_rotate_from), transform.xform(display_rotate_to), rotate_color);
+ if (drag_type == DRAG_ROTATE) {
+ // Draw the line when rotating a node
+ viewport->draw_line(transform.xform(drag_rotation_center), transform.xform(drag_to), rotate_color);
}
}
-void CanvasItemEditor::_draw_axis() {
+void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) {
+ // Draw a line going through the whole screen from a vector
RID ci = viewport->get_canvas_item();
+ Vector<Point2> points;
+ Point2 from = transform.xform(p_from);
+ Point2 to = transform.xform(p_to);
+ Size2 viewport_size = viewport->get_size();
+
+ if (to.x == from.x) {
+ // Vertical line
+ points.push_back(Point2(to.x, 0));
+ points.push_back(Point2(to.x, viewport_size.y));
+ } else if (to.y == from.y) {
+ // Horizontal line
+ points.push_back(Point2(0, to.y));
+ points.push_back(Point2(viewport_size.x, to.y));
+ } else {
+ float y_for_zero_x = (to.y * from.x - from.y * to.x) / (from.x - to.x);
+ float x_for_zero_y = (to.x * from.y - from.x * to.y) / (from.y - to.y);
+ float y_for_viewport_x = ((to.y - from.y) * (viewport_size.x - from.x)) / (to.x - from.x) + from.y;
+ float x_for_viewport_y = ((to.x - from.x) * (viewport_size.y - from.y)) / (to.y - from.y) + from.x; // faux
+
+ //bool start_set = false;
+ if (y_for_zero_x >= 0 && y_for_zero_x <= viewport_size.y) {
+ points.push_back(Point2(0, y_for_zero_x));
+ }
+ if (x_for_zero_y >= 0 && x_for_zero_y <= viewport_size.x) {
+ points.push_back(Point2(x_for_zero_y, 0));
+ }
+ if (y_for_viewport_x >= 0 && y_for_viewport_x <= viewport_size.y) {
+ points.push_back(Point2(viewport_size.x, y_for_viewport_x));
+ }
+ if (x_for_viewport_y >= 0 && x_for_viewport_y <= viewport_size.x) {
+ points.push_back(Point2(x_for_viewport_y, viewport_size.y));
+ }
+ }
+ if (points.size() >= 2) {
+ VisualServer::get_singleton()->canvas_item_add_line(ci, points[0], points[1], p_color);
+ }
+}
- Color x_axis_color(1.0, 0.4, 0.4, 0.6);
- Color y_axis_color(0.4, 1.0, 0.4, 0.6);
- Color area_axis_color(0.4, 0.4, 1.0, 0.4);
+void CanvasItemEditor::_draw_axis() {
- Point2 origin = transform.get_origin();
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(0, origin.y), Point2(viewport->get_size().x, origin.y), x_axis_color);
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(origin.x, 0), Point2(origin.x, viewport->get_size().y), y_axis_color);
+ if (show_origin) {
- Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Color x_axis_color(1.0, 0.4, 0.4, 0.6);
+ Color y_axis_color(0.4, 1.0, 0.4, 0.6);
- Vector2 screen_endpoints[4] = {
- transform.xform(Vector2(0, 0)),
- transform.xform(Vector2(screen_size.width, 0)),
- transform.xform(Vector2(screen_size.width, screen_size.height)),
- transform.xform(Vector2(0, screen_size.height))
- };
+ _draw_straight_line(Point2(), Point2(1, 0), x_axis_color);
+ _draw_straight_line(Point2(), Point2(0, 1), y_axis_color);
+ }
+
+ if (show_viewport) {
+
+ RID ci = viewport->get_canvas_item();
+
+ Color area_axis_color(0.4, 0.4, 1.0, 0.4);
- for (int i = 0; i < 4; i++) {
- VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color);
+ Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+
+ Vector2 screen_endpoints[4] = {
+ transform.xform(Vector2(0, 0)),
+ transform.xform(Vector2(screen_size.width, 0)),
+ transform.xform(Vector2(screen_size.width, screen_size.height)),
+ transform.xform(Vector2(0, screen_size.height))
+ };
+
+ for (int i = 0; i < 4; i++) {
+ VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color);
+ }
}
}
@@ -2776,43 +2583,52 @@ void CanvasItemEditor::_draw_bones() {
Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
+ int bone_outline_size = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size");
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- E->get().from = Vector2();
- E->get().to = Vector2();
+ Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from));
+ Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to));
- Node2D *n2d = Object::cast_to<Node2D>(ObjectDB::get_instance(E->get().bone));
- if (!n2d)
+ if (!from_node->is_inside_tree())
+ continue; //may have been removed
+ if (!from_node)
continue;
- if (!n2d->get_parent())
+ if (!to_node && E->get().length == 0)
continue;
- CanvasItem *pi = n2d->get_parent_item();
+ Vector2 from = transform.xform(from_node->get_global_position());
+ Vector2 to;
- Node2D *pn2d = Object::cast_to<Node2D>(n2d->get_parent());
-
- if (!pn2d)
- continue;
-
- Vector2 from = transform.xform(pn2d->get_global_position());
- Vector2 to = transform.xform(n2d->get_global_position());
-
- E->get().from = from;
- E->get().to = to;
+ if (to_node)
+ to = transform.xform(to_node->get_global_position());
+ else
+ to = transform.xform(from_node->get_global_transform().xform(Vector2(E->get().length, 0)));
Vector2 rel = to - from;
Vector2 relt = rel.tangent().normalized() * bone_width;
+ Vector2 reln = rel.normalized();
+ Vector2 reltn = relt.normalized();
Vector<Vector2> bone_shape;
bone_shape.push_back(from);
bone_shape.push_back(from + rel * 0.2 + relt);
bone_shape.push_back(to);
bone_shape.push_back(from + rel * 0.2 - relt);
+
+ Vector<Vector2> bone_shape_outline;
+ bone_shape_outline.push_back(from + (-reln - reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + (-reln + reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + rel * 0.2 + relt + reltn * bone_outline_size);
+ bone_shape_outline.push_back(to + (reln + reltn) * bone_outline_size);
+ bone_shape_outline.push_back(to + (reln - reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + rel * 0.2 - relt - reltn * bone_outline_size);
+
Vector<Color> colors;
- if (pi->has_meta("_edit_ik_")) {
+ if (from_node->has_meta("_edit_ik_")) {
colors.push_back(bone_ik_color);
colors.push_back(bone_ik_color);
@@ -2825,98 +2641,221 @@ void CanvasItemEditor::_draw_bones() {
colors.push_back(bone_color2);
}
+ Vector<Color> outline_colors;
+
+ if (editor_selection->is_selected(from_node)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_polygon(ci, bone_shape_outline, outline_colors);
VisualServer::get_singleton()->canvas_item_add_primitive(ci, bone_shape, colors, Vector<Vector2>(), RID());
+ }
+ }
+}
- if (editor_selection->is_selected(pi)) {
- for (int i = 0; i < bone_shape.size(); i++) {
+void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+ ERR_FAIL_COND(!p_node);
- VisualServer::get_singleton()->canvas_item_add_line(ci, bone_shape[i], bone_shape[(i + 1) % bone_shape.size()], bone_selected_color, 2);
- }
+ Node *scene = editor->get_edited_scene();
+ if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner()))
+ return;
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ if (canvas_item && !canvas_item->is_visible())
+ return;
+
+ Transform2D parent_xform = p_parent_xform;
+ Transform2D canvas_xform = p_canvas_xform;
+
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ parent_xform = parent_xform * canvas_item->get_transform();
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ parent_xform = Transform2D();
+ canvas_xform = cl ? cl->get_transform() : p_canvas_xform;
+ }
+
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ _draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform);
+ }
+
+ if (canvas_item && !canvas_item->_edit_use_rect() && !editor_selection->is_selected(canvas_item)) {
+ Transform2D xform = transform * canvas_xform * parent_xform;
+
+ // Draw the node's position
+ Ref<Texture> position_icon = get_icon("EditorPositionUnselected", "EditorIcons");
+ Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
+ viewport->draw_set_transform_matrix(transform);
+ viewport->draw_texture(position_icon, -position_icon->get_size() / 2, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_set_transform_matrix(Transform2D());
+ }
+}
+
+void CanvasItemEditor::_draw_hover() {
+ List<Rect2> previous_rects;
+
+ for (int i = 0; i < hovering_results.size(); i++) {
+ // Draw the node's name and icon
+ CanvasItem *canvas_item = hovering_results[i].item;
+
+ if (canvas_item->_edit_use_rect())
+ continue;
+
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+
+ // Get the resources
+ Ref<Texture> node_icon;
+ if (has_icon(canvas_item->get_class(), "EditorIcons"))
+ node_icon = get_icon(canvas_item->get_class(), "EditorIcons");
+ else
+ node_icon = get_icon("Object", "EditorIcons");
+ Ref<Font> font = get_font("font", "Label");
+ String node_name = canvas_item->get_name();
+ Size2 node_name_size = font->get_string_size(node_name);
+ Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3));
+
+ Point2 pos = xform.get_origin() - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
+ // Rectify the position to avoid overlaping items
+ for (List<Rect2>::Element *E = previous_rects.front(); E; E = E->next()) {
+ if (E->get().intersects(Rect2(pos, item_size))) {
+ pos.y = E->get().get_position().y - item_size.y;
}
}
+
+ previous_rects.push_back(Rect2(pos, item_size));
+
+ // Draw the node icon and name
+ viewport->draw_texture(node_icon, pos, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, Color(1.0, 1.0, 1.0, 0.5));
}
}
-void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_xform) {
+void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
ERR_FAIL_COND(!p_node);
- RID viewport_ci = viewport->get_canvas_item();
+ Node *scene = editor->get_edited_scene();
+ if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner()))
+ return;
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ if (canvas_item && !canvas_item->is_visible())
+ return;
- Transform2D transform_ci = p_xform;
- CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
- if (ci)
- transform_ci = transform_ci * ci->get_transform();
+ Transform2D parent_xform = p_parent_xform;
+ Transform2D canvas_xform = p_canvas_xform;
+
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ parent_xform = parent_xform * canvas_item->get_transform();
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ parent_xform = Transform2D();
+ canvas_xform = cl ? cl->get_transform() : p_canvas_xform;
+ }
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- _draw_locks_and_groups(p_node->get_child(i), transform_ci);
+ _draw_locks_and_groups(p_node->get_child(i), parent_xform, canvas_xform);
}
- if (ci) {
+ RID viewport_canvas_item = viewport->get_canvas_item();
+ if (canvas_item) {
+ float offset = 0;
+
Ref<Texture> lock = get_icon("LockViewport", "EditorIcons");
if (p_node->has_meta("_edit_lock_")) {
- lock->draw(viewport_ci, transform_ci.xform(Point2(0, 0)));
+ lock->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0));
+ offset += lock->get_size().x;
}
Ref<Texture> group = get_icon("GroupViewport", "EditorIcons");
- if (ci->has_meta("_edit_group_")) {
- Vector2 ofs = transform_ci.xform(Point2(0, 0));
- if (ci->has_meta("_edit_lock_"))
- ofs = Point2(ofs.x + lock->get_size().x, ofs.y);
- group->draw(viewport_ci, ofs);
+ if (canvas_item->has_meta("_edit_group_")) {
+ group->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0));
+ //offset += group->get_size().x;
}
}
}
-void CanvasItemEditor::_build_bones_list(Node *p_node) {
- ERR_FAIL_COND(!p_node);
+bool CanvasItemEditor::_build_bones_list(Node *p_node) {
+ ERR_FAIL_COND_V(!p_node, false);
+
+ bool has_child_bones = false;
for (int i = 0; i < p_node->get_child_count(); i++) {
- _build_bones_list(p_node->get_child(i));
+ if (_build_bones_list(p_node->get_child(i))) {
+ has_child_bones = true;
+ }
+ }
+
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ Node *scene = editor->get_edited_scene();
+ if (!canvas_item || !canvas_item->is_visible() || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner()))) {
+ return false;
}
- CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
- if (c && c->is_visible_in_tree()) {
- if (c->has_meta("_edit_bone_")) {
+ Node *parent = canvas_item->get_parent();
- ObjectID id = c->get_instance_id();
- if (!bone_list.has(id)) {
- BoneList bone;
- bone.bone = id;
- bone_list[id] = bone;
+ if (Object::cast_to<Bone2D>(canvas_item)) {
+ if (Object::cast_to<Bone2D>(parent)) {
+ // Add as bone->parent relationship
+ BoneKey bk;
+ bk.from = parent->get_instance_id();
+ bk.to = canvas_item->get_instance_id();
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
}
- bone_list[id].last_pass = bone_last_frame;
+ bone_list[bk].last_pass = bone_last_frame;
}
- }
-}
-void CanvasItemEditor::_get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform) {
- ERR_FAIL_COND(!p_node);
+ if (!has_child_bones) {
+ // Add a last bone if the Bone2D has no Bone2D child
+ BoneKey bk;
+ bk.from = canvas_item->get_instance_id();
+ bk.to = 0;
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
+ }
+ bone_list[bk].last_pass = bone_last_frame;
+ }
- for (int i = 0; i < p_node->get_child_count(); i++) {
- _get_encompassing_rect(p_node->get_child(i), r_rect, p_xform);
+ return true;
}
- CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
- if (c && c->is_visible_in_tree()) {
- Rect2 rect = c->_edit_get_rect();
- Transform2D xform = p_xform * c->get_transform();
- r_rect.expand_to(xform.xform(rect.position));
- r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
- r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y)));
- r_rect.expand_to(xform.xform(rect.position + rect.size));
+ if (canvas_item->has_meta("_edit_bone_")) {
+ // Add a "custom bone"
+ BoneKey bk;
+ bk.from = parent->get_instance_id();
+ bk.to = canvas_item->get_instance_id();
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
+ }
+ bone_list[bk].last_pass = bone_last_frame;
}
-}
-void CanvasItemEditor::_draw_viewport_base() {
- if (show_rulers)
- _draw_rulers();
- if (show_guides)
- _draw_guides();
- _draw_focus();
+ return false;
}
void CanvasItemEditor::_draw_viewport() {
+ // Update the transform
+ transform = Transform2D();
+ transform.scale_basis(Size2(zoom, zoom));
+ transform.elements[2] = -view_offset * zoom;
+ editor->get_scene_root()->set_global_canvas_transform(transform);
// hide/show buttons depending on the selection
bool all_locked = true;
@@ -2947,13 +2886,13 @@ void CanvasItemEditor::_draw_viewport() {
group_button->set_disabled(selection.empty());
ungroup_button->set_visible(all_group);
- _update_scrollbars();
-
_draw_grid();
_draw_selection();
_draw_axis();
- if (editor->get_edited_scene())
- _draw_locks_and_groups(editor->get_edited_scene(), transform);
+ if (editor->get_edited_scene()) {
+ _draw_locks_and_groups(editor->get_edited_scene());
+ _draw_invisible_nodes_positions(editor->get_edited_scene());
+ }
RID ci = viewport->get_canvas_item();
VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D());
@@ -2968,76 +2907,79 @@ void CanvasItemEditor::_draw_viewport() {
}
_draw_bones();
+ if (show_rulers)
+ _draw_rulers();
+ if (show_guides)
+ _draw_guides();
+ _draw_focus();
+ _draw_hover();
}
void CanvasItemEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
-
EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels"));
- List<Node *> selection = editor_selection->get_selected_node_list();
-
- bool all_control = true;
- bool has_control = false;
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
- if (!canvas_item || !canvas_item->is_visible_in_tree())
- continue;
-
- if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
- continue;
-
- if (Object::cast_to<Control>(canvas_item))
- has_control = true;
- else
- all_control = false;
+ int nb_control = 0;
+ int nb_having_pivot = 0;
+ // Update the viewport if the canvas_item changes
+ List<CanvasItem *> selection = _get_edited_canvas_items();
+ for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- if (!se)
- continue;
- Rect2 r = canvas_item->_edit_get_rect();
+ Rect2 rect;
+ if (canvas_item->_edit_use_rect()) {
+ rect = canvas_item->_edit_get_rect();
+ } else {
+ rect = Rect2();
+ }
Transform2D xform = canvas_item->get_transform();
- if (r != se->prev_rect || xform != se->prev_xform) {
+ if (rect != se->prev_rect || xform != se->prev_xform) {
viewport->update();
- se->prev_rect = r;
+ se->prev_rect = rect;
se->prev_xform = xform;
}
- if (Object::cast_to<Control>(canvas_item)) {
+ Control *control = Object::cast_to<Control>(canvas_item);
+ if (control) {
float anchors[4];
Vector2 pivot;
- pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
- anchors[MARGIN_LEFT] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_LEFT);
- anchors[MARGIN_RIGHT] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_RIGHT);
- anchors[MARGIN_TOP] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_TOP);
- anchors[MARGIN_BOTTOM] = Object::cast_to<Control>(canvas_item)->get_anchor(MARGIN_BOTTOM);
+ pivot = control->get_pivot_offset();
+ anchors[MARGIN_LEFT] = control->get_anchor(MARGIN_LEFT);
+ anchors[MARGIN_RIGHT] = control->get_anchor(MARGIN_RIGHT);
+ anchors[MARGIN_TOP] = control->get_anchor(MARGIN_TOP);
+ anchors[MARGIN_BOTTOM] = control->get_anchor(MARGIN_BOTTOM);
if (pivot != se->prev_pivot || anchors[MARGIN_LEFT] != se->prev_anchors[MARGIN_LEFT] || anchors[MARGIN_RIGHT] != se->prev_anchors[MARGIN_RIGHT] || anchors[MARGIN_TOP] != se->prev_anchors[MARGIN_TOP] || anchors[MARGIN_BOTTOM] != se->prev_anchors[MARGIN_BOTTOM]) {
- viewport->update();
- viewport_base->update();
se->prev_pivot = pivot;
se->prev_anchors[MARGIN_LEFT] = anchors[MARGIN_LEFT];
se->prev_anchors[MARGIN_RIGHT] = anchors[MARGIN_RIGHT];
se->prev_anchors[MARGIN_TOP] = anchors[MARGIN_TOP];
se->prev_anchors[MARGIN_BOTTOM] = anchors[MARGIN_BOTTOM];
+ viewport->update();
}
+ nb_control++;
+ }
+
+ if (canvas_item->_edit_use_pivot()) {
+ nb_having_pivot++;
}
}
- if (all_control && has_control)
- presets_menu->show();
- else
- presets_menu->hide();
+ // Activate / Deactivate the pivot tool
+ pivot_button->set_disabled(nb_having_pivot == 0);
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ // Show / Hide the layout button
+ presets_menu->set_visible(nb_control > 0 && nb_control == selection.size());
- Object *b = ObjectDB::get_instance(E->get().bone);
+ // Update the viewport if bones changes
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+
+ Object *b = ObjectDB::get_instance(E->key().from);
if (!b) {
viewport->update();
@@ -3045,13 +2987,22 @@ void CanvasItemEditor::_notification(int p_what) {
}
Node2D *b2 = Object::cast_to<Node2D>(b);
- if (!b2) {
+ if (!b2 || !b2->is_inside_tree()) {
continue;
}
- if (b2->get_global_transform() != E->get().xform) {
+ Transform2D global_xform = b2->get_global_transform();
+
+ if (global_xform != E->get().xform) {
- E->get().xform = b2->get_global_transform();
+ E->get().xform = global_xform;
+ viewport->update();
+ }
+
+ Bone2D *bone = Object::cast_to<Bone2D>(b);
+ if (bone && bone->get_default_length() != E->get().length) {
+
+ E->get().length = bone->get_default_length();
viewport->update();
}
}
@@ -3067,12 +3018,19 @@ void CanvasItemEditor::_notification(int p_what) {
AnimationPlayerEditor::singleton->get_key_editor()->connect("visibility_changed", this, "_keying_changed");
_keying_changed();
+ get_tree()->connect("node_added", this, "_tree_changed", varray());
+ get_tree()->connect("node_removed", this, "_tree_changed", varray());
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
select_sb->set_texture(get_icon("EditorRect2D", "EditorIcons"));
}
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ get_tree()->disconnect("node_added", this, "_tree_changed");
+ get_tree()->disconnect("node_removed", this, "_tree_changed");
+ }
+
if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
select_button->set_icon(get_icon("ToolSelect", "EditorIcons"));
list_select_button->set_icon(get_icon("ListSelect", "EditorIcons"));
@@ -3150,108 +3108,140 @@ void CanvasItemEditor::_notification(int p_what) {
void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
- drag = DRAG_NONE;
+ drag_type = DRAG_NONE;
// Clear the selection
editor_selection->clear(); //_clear_canvas_items();
editor_selection->add_node(p_canvas_item);
- //_add_canvas_item(p_canvas_item);
- viewport->update();
- viewport_base->update();
}
-void CanvasItemEditor::_update_scrollbars() {
-
- updating_scroll = true;
-
- if (show_rulers)
- viewport_scrollable->set_begin(Point2(RULER_WIDTH, RULER_WIDTH));
- else
- viewport_scrollable->set_begin(Point2());
-
- Size2 size = viewport->get_size();
- Size2 hmin = h_scroll->get_minimum_size();
- Size2 vmin = v_scroll->get_minimum_size();
-
- v_scroll->set_begin(Point2(size.width - vmin.width, 0));
- v_scroll->set_end(Point2(size.width, size.height));
-
- h_scroll->set_begin(Point2(0, size.height - hmin.height));
- h_scroll->set_end(Point2(size.width - vmin.width, size.height));
+void CanvasItemEditor::_queue_update_bone_list() {
- Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ if (bone_list_dirty)
+ return;
- Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height));
+ call_deferred("_update_bone_list");
+ bone_list_dirty = true;
+}
- Rect2 canvas_item_rect = Rect2(Point2(), screen_rect);
+void CanvasItemEditor::_update_bone_list() {
bone_last_frame++;
if (editor->get_edited_scene()) {
_build_bones_list(editor->get_edited_scene());
- _get_encompassing_rect(editor->get_edited_scene(), canvas_item_rect, Transform2D());
}
- List<Map<ObjectID, BoneList>::Element *> bone_to_erase;
-
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
-
+ List<Map<BoneKey, BoneList>::Element *> bone_to_erase;
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
if (E->get().last_pass != bone_last_frame) {
bone_to_erase.push_back(E);
+ continue;
}
- }
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E->key().from));
+ if (!node || !node->is_inside_tree() || (node != get_tree()->get_edited_scene_root() && !get_tree()->get_edited_scene_root()->is_a_parent_of(node))) {
+ bone_to_erase.push_back(E);
+ continue;
+ }
+ }
while (bone_to_erase.size()) {
bone_list.erase(bone_to_erase.front()->get());
bone_to_erase.pop_front();
}
+ bone_list_dirty = false;
+}
+
+void CanvasItemEditor::_tree_changed(Node *) {
+ _queue_update_bone_list();
+}
+
+void CanvasItemEditor::_update_scrollbars() {
+
+ updating_scroll = true;
+
+ // Move the zoom buttons
+ Point2 zoom_hb_begin = Point2(5, 5);
+ zoom_hb_begin += (show_rulers) ? Point2(RULER_WIDTH, RULER_WIDTH) : Point2();
+ zoom_hb->set_begin(zoom_hb_begin);
+
+ // Move and resize the scrollbars
+ Size2 size = viewport->get_size();
+ Size2 hmin = h_scroll->get_minimum_size();
+ Size2 vmin = v_scroll->get_minimum_size();
+
+ v_scroll->set_begin(Point2(size.width - vmin.width, (show_rulers) ? RULER_WIDTH : 0));
+ v_scroll->set_end(Point2(size.width, size.height));
+
+ h_scroll->set_begin(Point2((show_rulers) ? RULER_WIDTH : 0, size.height - hmin.height));
+ h_scroll->set_end(Point2(size.width - vmin.width, size.height));
- //expand area so it's easier to do animations and stuff at 0,0
+ // Get the visible frame
+ Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height));
+
+ _queue_update_bone_list();
+
+ // Calculate scrollable area
+ Rect2 canvas_item_rect = Rect2(Point2(), screen_rect);
+ if (editor->get_edited_scene()) {
+ Rect2 content_rect = _get_encompassing_rect(editor->get_edited_scene());
+ canvas_item_rect.expand_to(content_rect.position);
+ canvas_item_rect.expand_to(content_rect.position + content_rect.size);
+ }
canvas_item_rect.size += screen_rect * 2;
canvas_item_rect.position -= screen_rect;
- Point2 ofs;
+ // Constraints the view offset and updates the scrollbars
+ Point2 begin = canvas_item_rect.position;
+ Point2 end = canvas_item_rect.position + canvas_item_rect.size - local_rect.size / zoom;
if (canvas_item_rect.size.height <= (local_rect.size.y / zoom)) {
+ if (ABS(begin.y - previous_update_view_offset.y) < ABS(begin.y - view_offset.y)) {
+ view_offset.y = previous_update_view_offset.y;
+ }
+
v_scroll->hide();
- ofs.y = canvas_item_rect.position.y;
} else {
-
- v_scroll->show();
- v_scroll->set_min(canvas_item_rect.position.y);
- v_scroll->set_max(canvas_item_rect.position.y + canvas_item_rect.size.y);
- v_scroll->set_page(local_rect.size.y / zoom);
- if (first_update) {
- //so 0,0 is visible
- v_scroll->set_value(-10);
- h_scroll->set_value(-10);
- first_update = false;
+ if (view_offset.y > end.y && view_offset.y > previous_update_view_offset.y) {
+ view_offset.y = MAX(end.y, previous_update_view_offset.y);
+ }
+ if (view_offset.y < begin.y && view_offset.y < previous_update_view_offset.y) {
+ view_offset.y = MIN(begin.y, previous_update_view_offset.y);
}
- ofs.y = v_scroll->get_value();
+ v_scroll->show();
+ v_scroll->set_min(MIN(view_offset.y, begin.y));
+ v_scroll->set_max(MAX(view_offset.y, end.y) + screen_rect.y);
+ v_scroll->set_page(screen_rect.y);
}
if (canvas_item_rect.size.width <= (local_rect.size.x / zoom)) {
+ if (ABS(begin.x - previous_update_view_offset.x) < ABS(begin.x - view_offset.x)) {
+ view_offset.x = previous_update_view_offset.x;
+ }
h_scroll->hide();
- ofs.x = canvas_item_rect.position.x;
} else {
+ if (view_offset.x > end.x && view_offset.x > previous_update_view_offset.x) {
+ view_offset.x = MAX(end.x, previous_update_view_offset.x);
+ }
+ if (view_offset.x < begin.x && view_offset.x < previous_update_view_offset.x) {
+ view_offset.x = MIN(begin.x, previous_update_view_offset.x);
+ }
h_scroll->show();
- h_scroll->set_min(canvas_item_rect.position.x);
- h_scroll->set_max(canvas_item_rect.position.x + canvas_item_rect.size.x);
- h_scroll->set_page(local_rect.size.x / zoom);
- ofs.x = h_scroll->get_value();
+ h_scroll->set_min(MIN(view_offset.x, begin.x));
+ h_scroll->set_max(MAX(view_offset.x, end.x) + screen_rect.x);
+ h_scroll->set_page(screen_rect.x);
}
- //transform=Matrix32();
- transform.elements[2] = -ofs * zoom;
-
- editor->get_scene_root()->set_global_canvas_transform(transform);
+ // Calculate scrollable area
+ v_scroll->set_value(view_offset.y);
+ h_scroll->set_value(view_offset.x);
+ previous_update_view_offset = view_offset;
updating_scroll = false;
-
- //transform.scale_basis(Vector2(zoom,zoom));
}
void CanvasItemEditor::_update_scroll(float) {
@@ -3259,62 +3249,45 @@ void CanvasItemEditor::_update_scroll(float) {
if (updating_scroll)
return;
- Point2 ofs;
- ofs.x = h_scroll->get_value();
- ofs.y = v_scroll->get_value();
-
- //current_window->set_scroll(-ofs);
-
- transform = Transform2D();
-
- transform.scale_basis(Size2(zoom, zoom));
- transform.elements[2] = -ofs;
-
- editor->get_scene_root()->set_global_canvas_transform(transform);
-
+ view_offset.x = h_scroll->get_value();
+ view_offset.y = v_scroll->get_value();
viewport->update();
- viewport_base->update();
}
void CanvasItemEditor::_set_anchors_and_margins_preset(Control::LayoutPreset p_preset) {
List<Node *> selection = editor_selection->get_selected_node_list();
undo_redo->create_action(TTR("Change Anchors and Margins"));
+
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
- Control *c = Object::cast_to<Control>(E->get());
-
- undo_redo->add_do_method(c, "set_anchors_preset", p_preset);
- switch (p_preset) {
- case PRESET_TOP_LEFT:
- case PRESET_TOP_RIGHT:
- case PRESET_BOTTOM_LEFT:
- case PRESET_BOTTOM_RIGHT:
- case PRESET_CENTER_LEFT:
- case PRESET_CENTER_TOP:
- case PRESET_CENTER_RIGHT:
- case PRESET_CENTER_BOTTOM:
- case PRESET_CENTER:
- undo_redo->add_do_method(c, "set_margins_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE);
- break;
- case PRESET_LEFT_WIDE:
- case PRESET_TOP_WIDE:
- case PRESET_RIGHT_WIDE:
- case PRESET_BOTTOM_WIDE:
- case PRESET_VCENTER_WIDE:
- case PRESET_HCENTER_WIDE:
- case PRESET_WIDE:
- undo_redo->add_do_method(c, "set_margins_preset", p_preset, Control::PRESET_MODE_MINSIZE);
- break;
+ Control *control = Object::cast_to<Control>(E->get());
+ if (control) {
+ undo_redo->add_do_method(control, "set_anchors_preset", p_preset);
+ switch (p_preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_TOP_RIGHT:
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_LEFT:
+ case PRESET_CENTER_TOP:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_CENTER:
+ undo_redo->add_do_method(control, "set_margins_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE);
+ break;
+ case PRESET_LEFT_WIDE:
+ case PRESET_TOP_WIDE:
+ case PRESET_RIGHT_WIDE:
+ case PRESET_BOTTOM_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ undo_redo->add_do_method(control, "set_margins_preset", p_preset, Control::PRESET_MODE_MINSIZE);
+ break;
+ }
+ undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
}
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_LEFT, c->get_anchor(MARGIN_LEFT));
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_TOP, c->get_anchor(MARGIN_TOP));
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_RIGHT, c->get_anchor(MARGIN_RIGHT));
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_BOTTOM, c->get_anchor(MARGIN_BOTTOM));
- undo_redo->add_undo_method(c, "set_margin", MARGIN_LEFT, c->get_margin(MARGIN_LEFT));
- undo_redo->add_undo_method(c, "set_margin", MARGIN_TOP, c->get_margin(MARGIN_TOP));
- undo_redo->add_undo_method(c, "set_margin", MARGIN_RIGHT, c->get_margin(MARGIN_RIGHT));
- undo_redo->add_undo_method(c, "set_margin", MARGIN_BOTTOM, c->get_margin(MARGIN_BOTTOM));
}
undo_redo->commit_action();
@@ -3326,13 +3299,11 @@ void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) {
undo_redo->create_action(TTR("Change Anchors"));
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
- Control *c = Object::cast_to<Control>(E->get());
-
- undo_redo->add_do_method(c, "set_anchors_preset", p_preset);
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_LEFT, c->get_anchor(MARGIN_LEFT));
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_TOP, c->get_anchor(MARGIN_TOP));
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_RIGHT, c->get_anchor(MARGIN_RIGHT));
- undo_redo->add_undo_method(c, "set_anchor", MARGIN_BOTTOM, c->get_anchor(MARGIN_BOTTOM));
+ Control *control = Object::cast_to<Control>(E->get());
+ if (control) {
+ undo_redo->add_do_method(control, "set_anchors_preset", p_preset);
+ undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state());
+ }
}
undo_redo->commit_action();
@@ -3346,30 +3317,39 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
zoom = p_zoom;
Point2 ofs = p_position;
ofs = ofs / prev_zoom - ofs / zoom;
- h_scroll->set_value(Math::round(h_scroll->get_value() + ofs.x));
- v_scroll->set_value(Math::round(v_scroll->get_value() + ofs.y));
+ view_offset.x = Math::round(view_offset.x + ofs.x);
+ view_offset.y = Math::round(view_offset.y + ofs.y);
- _update_scroll(0);
+ _update_scrollbars();
viewport->update();
- viewport_base->update();
}
-void CanvasItemEditor::_zoom_minus() {
- _zoom_on_position(zoom / 2.0, viewport_scrollable->get_size() / 2.0);
+void CanvasItemEditor::_button_zoom_minus() {
+ _zoom_on_position(zoom / 1.5, viewport_scrollable->get_size() / 2.0);
}
-void CanvasItemEditor::_zoom_reset() {
+void CanvasItemEditor::_button_zoom_reset() {
_zoom_on_position(1.0, viewport_scrollable->get_size() / 2.0);
}
-void CanvasItemEditor::_zoom_plus() {
- _zoom_on_position(zoom * 2.0, viewport_scrollable->get_size() / 2.0);
+void CanvasItemEditor::_button_zoom_plus() {
+ _zoom_on_position(zoom * 1.5, viewport_scrollable->get_size() / 2.0);
}
-void CanvasItemEditor::_toggle_snap(bool p_status) {
+void CanvasItemEditor::_button_toggle_snap(bool p_status) {
snap_active = p_status;
viewport->update();
- viewport_base->update();
+}
+
+void CanvasItemEditor::_button_tool_select(int p_index) {
+
+ ToolButton *tb[TOOL_MAX] = { select_button, list_select_button, move_button, rotate_button, pivot_button, pan_button };
+ for (int i = 0; i < TOOL_MAX; i++) {
+ tb[i]->set_pressed(i == p_index);
+ }
+
+ viewport->update();
+ tool = (Tool)p_index;
}
void CanvasItemEditor::_popup_callback(int p_op) {
@@ -3382,7 +3362,18 @@ void CanvasItemEditor::_popup_callback(int p_op) {
int idx = view_menu->get_popup()->get_item_index(SHOW_GRID);
view_menu->get_popup()->set_item_checked(idx, show_grid);
viewport->update();
- viewport_base->update();
+ } break;
+ case SHOW_ORIGIN: {
+ show_origin = !show_origin;
+ int idx = view_menu->get_popup()->get_item_index(SHOW_ORIGIN);
+ view_menu->get_popup()->set_item_checked(idx, show_origin);
+ viewport->update();
+ } break;
+ case SHOW_VIEWPORT: {
+ show_viewport = !show_viewport;
+ int idx = view_menu->get_popup()->get_item_index(SHOW_VIEWPORT);
+ view_menu->get_popup()->set_item_checked(idx, show_viewport);
+ viewport->update();
} break;
case SNAP_USE_NODE_PARENT: {
snap_node_parent = !snap_node_parent;
@@ -3399,6 +3390,11 @@ void CanvasItemEditor::_popup_callback(int p_op) {
int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES);
smartsnap_config_popup->set_item_checked(idx, snap_node_sides);
} break;
+ case SNAP_USE_NODE_CENTER: {
+ snap_node_center = !snap_node_center;
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_CENTER);
+ smartsnap_config_popup->set_item_checked(idx, snap_node_center);
+ } break;
case SNAP_USE_OTHER_NODES: {
snap_other_nodes = !snap_other_nodes;
int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES);
@@ -3424,7 +3420,6 @@ void CanvasItemEditor::_popup_callback(int p_op) {
int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE);
snap_config_menu->get_popup()->set_item_checked(idx, snap_relative);
viewport->update();
- viewport_base->update();
} break;
case SNAP_USE_PIXEL: {
snap_pixel = !snap_pixel;
@@ -3452,26 +3447,19 @@ void CanvasItemEditor::_popup_callback(int p_op) {
int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
view_menu->get_popup()->set_item_checked(idx, show_rulers);
viewport->update();
- viewport_base->update();
} break;
case SHOW_GUIDES: {
show_guides = !show_guides;
int idx = view_menu->get_popup()->get_item_index(SHOW_GUIDES);
view_menu->get_popup()->set_item_checked(idx, show_guides);
viewport->update();
- viewport_base->update();
} break;
-
case LOCK_SELECTED: {
-
List<Node *> selection = editor_selection->get_selected_node_list();
-
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
if (!canvas_item || !canvas_item->is_inside_tree())
continue;
-
if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
continue;
@@ -3481,35 +3469,25 @@ void CanvasItemEditor::_popup_callback(int p_op) {
viewport->update();
} break;
case UNLOCK_SELECTED: {
-
List<Node *> selection = editor_selection->get_selected_node_list();
-
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
if (!canvas_item || !canvas_item->is_inside_tree())
continue;
-
if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
continue;
canvas_item->set_meta("_edit_lock_", Variant());
emit_signal("item_lock_status_changed");
}
-
viewport->update();
-
} break;
case GROUP_SELECTED: {
-
List<Node *> selection = editor_selection->get_selected_node_list();
-
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
if (!canvas_item || !canvas_item->is_inside_tree())
continue;
-
if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
continue;
@@ -3519,26 +3497,19 @@ void CanvasItemEditor::_popup_callback(int p_op) {
viewport->update();
} break;
case UNGROUP_SELECTED: {
-
List<Node *> selection = editor_selection->get_selected_node_list();
-
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
-
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
if (!canvas_item || !canvas_item->is_inside_tree())
continue;
-
if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
continue;
canvas_item->set_meta("_edit_group_", Variant());
emit_signal("item_group_status_changed");
}
-
viewport->update();
-
} break;
-
case ANCHORS_AND_MARGINS_PRESET_TOP_LEFT: {
_set_anchors_and_margins_preset(PRESET_TOP_LEFT);
} break;
@@ -3723,28 +3694,6 @@ void CanvasItemEditor::_popup_callback(int p_op) {
key_scale = key_scale_button->is_pressed();
} break;
- /*
- case ANIM_INSERT_POS_ROT
- case ANIM_INSERT_POS_SCALE:
- case ANIM_INSERT_ROT_SCALE:
- case ANIM_INSERT_POS_ROT_SCALE: {
- static const bool key_toggles[7][3]={
- {true,false,false},
- {false,true,false},
- {false,false,true},
- {true,true,false},
- {true,false,true},
- {false,true,true},
- {true,true,true}
- };
- key_pos=key_toggles[p_op-ANIM_INSERT_POS][0];
- key_rot=key_toggles[p_op-ANIM_INSERT_POS][1];
- key_scale=key_toggles[p_op-ANIM_INSERT_POS][2];
- for(int i=ANIM_INSERT_POS;i<=ANIM_INSERT_POS_ROT_SCALE;i++) {
- int idx = animation_menu->get_popup()->get_item_index(i);
- animation_menu->get_popup()->set_item_checked(idx,i==p_op);
- }
- } break;*/
case ANIM_COPY_POSE: {
pose_clipboard.clear();
@@ -3823,9 +3772,9 @@ void CanvasItemEditor::_popup_callback(int p_op) {
if (key_pos)
ctrl->set_position(Point2());
/*
- if (key_scale)
- AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
- */
+ if (key_scale)
+ AnimationPlayerEditor::singleton->get_key_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
+ */
}
}
@@ -3935,7 +3884,12 @@ void CanvasItemEditor::_focus_selection(int p_op) {
//if (!canvas_item->is_visible_in_tree()) continue;
++count;
- Rect2 item_rect = canvas_item->_edit_get_rect();
+ Rect2 item_rect;
+ if (canvas_item->_edit_use_rect()) {
+ item_rect = canvas_item->_edit_get_rect();
+ } else {
+ item_rect = Rect2();
+ }
Vector2 pos = canvas_item->get_global_transform().get_origin();
Vector2 scale = canvas_item->get_global_transform().get_scale();
@@ -3956,8 +3910,10 @@ void CanvasItemEditor::_focus_selection(int p_op) {
center = rect.position + rect.size / 2;
Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center);
- h_scroll->set_value(h_scroll->get_value() - offset.x / zoom);
- v_scroll->set_value(v_scroll->get_value() - offset.y / zoom);
+ view_offset.x -= offset.x / zoom;
+ view_offset.y -= offset.y / zoom;
+ _update_scrollbars();
+ viewport->update();
} else { // VIEW_FRAME_TO_SELECTION
@@ -3966,7 +3922,7 @@ void CanvasItemEditor::_focus_selection(int p_op) {
float scale_y = viewport->get_size().y / rect.size.y;
zoom = scale_x < scale_y ? scale_x : scale_y;
zoom *= 0.90;
- _update_scroll(0);
+ viewport->update();
call_deferred("_popup_callback", VIEW_CENTER_TO_SELECTION);
}
}
@@ -3974,21 +3930,23 @@ void CanvasItemEditor::_focus_selection(int p_op) {
void CanvasItemEditor::_bind_methods() {
- ClassDB::bind_method("_zoom_minus", &CanvasItemEditor::_zoom_minus);
- ClassDB::bind_method("_zoom_reset", &CanvasItemEditor::_zoom_reset);
- ClassDB::bind_method("_zoom_plus", &CanvasItemEditor::_zoom_plus);
- ClassDB::bind_method("_toggle_snap", &CanvasItemEditor::_toggle_snap);
+ ClassDB::bind_method("_button_zoom_minus", &CanvasItemEditor::_button_zoom_minus);
+ ClassDB::bind_method("_button_zoom_reset", &CanvasItemEditor::_button_zoom_reset);
+ ClassDB::bind_method("_button_zoom_plus", &CanvasItemEditor::_button_zoom_plus);
+ ClassDB::bind_method("_button_toggle_snap", &CanvasItemEditor::_button_toggle_snap);
ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll);
+ ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars);
ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback);
ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data);
- ClassDB::bind_method("_tool_select", &CanvasItemEditor::_tool_select);
+ ClassDB::bind_method("_button_tool_select", &CanvasItemEditor::_button_tool_select);
ClassDB::bind_method("_keying_changed", &CanvasItemEditor::_keying_changed);
ClassDB::bind_method("_unhandled_key_input", &CanvasItemEditor::_unhandled_key_input);
ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport);
- ClassDB::bind_method("_draw_viewport_base", &CanvasItemEditor::_draw_viewport_base);
ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport);
- ClassDB::bind_method("_gui_input_viewport_base", &CanvasItemEditor::_gui_input_viewport_base);
ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed);
+ ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
+ ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed);
+
ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
@@ -3997,11 +3955,185 @@ void CanvasItemEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_group_status_changed"));
}
+Dictionary CanvasItemEditor::get_state() const {
+
+ Dictionary state;
+ state["zoom"] = zoom;
+ state["ofs"] = view_offset;
+ state["grid_offset"] = grid_offset;
+ state["grid_step"] = grid_step;
+ state["snap_rotation_offset"] = snap_rotation_offset;
+ state["snap_rotation_step"] = snap_rotation_step;
+ state["snap_active"] = snap_active;
+ state["snap_node_parent"] = snap_node_parent;
+ state["snap_node_anchors"] = snap_node_anchors;
+ state["snap_node_sides"] = snap_node_sides;
+ state["snap_node_center"] = snap_node_center;
+ state["snap_other_nodes"] = snap_other_nodes;
+ state["snap_grid"] = snap_grid;
+ state["snap_guides"] = snap_guides;
+ state["show_grid"] = show_grid;
+ state["show_origin"] = show_origin;
+ state["show_viewport"] = show_viewport;
+ state["show_rulers"] = show_rulers;
+ state["show_guides"] = show_guides;
+ state["show_helpers"] = show_helpers;
+ state["snap_rotation"] = snap_rotation;
+ state["snap_relative"] = snap_relative;
+ state["snap_pixel"] = snap_pixel;
+ state["skeleton_show_bones"] = skeleton_show_bones;
+ return state;
+}
+
+void CanvasItemEditor::set_state(const Dictionary &p_state) {
+
+ Dictionary state = p_state;
+ if (state.has("zoom")) {
+ zoom = p_state["zoom"];
+ }
+
+ if (state.has("ofs")) {
+ view_offset = p_state["ofs"];
+ previous_update_view_offset = view_offset;
+ _update_scrollbars();
+ }
+
+ if (state.has("grid_offset")) {
+ grid_offset = state["grid_offset"];
+ }
+
+ if (state.has("grid_step")) {
+ grid_step = state["grid_step"];
+ }
+
+ if (state.has("snap_rotation_step")) {
+ snap_rotation_step = state["snap_rotation_step"];
+ }
+
+ if (state.has("snap_rotation_offset")) {
+ snap_rotation_offset = state["snap_rotation_offset"];
+ }
+
+ if (state.has("snap_active")) {
+ snap_active = state["snap_active"];
+ snap_button->set_pressed(snap_active);
+ }
+
+ if (state.has("snap_node_parent")) {
+ snap_node_parent = state["snap_node_parent"];
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT);
+ smartsnap_config_popup->set_item_checked(idx, snap_node_parent);
+ }
+
+ if (state.has("snap_node_anchors")) {
+ snap_node_anchors = state["snap_node_anchors"];
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_ANCHORS);
+ smartsnap_config_popup->set_item_checked(idx, snap_node_anchors);
+ }
+
+ if (state.has("snap_node_sides")) {
+ snap_node_sides = state["snap_node_sides"];
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_SIDES);
+ smartsnap_config_popup->set_item_checked(idx, snap_node_sides);
+ }
+
+ if (state.has("snap_node_center")) {
+ snap_node_center = state["snap_node_center"];
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_CENTER);
+ smartsnap_config_popup->set_item_checked(idx, snap_node_center);
+ }
+
+ if (state.has("snap_other_nodes")) {
+ snap_other_nodes = state["snap_other_nodes"];
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_OTHER_NODES);
+ smartsnap_config_popup->set_item_checked(idx, snap_other_nodes);
+ }
+
+ if (state.has("snap_guides")) {
+ snap_guides = state["snap_guides"];
+ int idx = smartsnap_config_popup->get_item_index(SNAP_USE_GUIDES);
+ smartsnap_config_popup->set_item_checked(idx, snap_guides);
+ }
+
+ if (state.has("snap_grid")) {
+ snap_grid = state["snap_grid"];
+ int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID);
+ snap_config_menu->get_popup()->set_item_checked(idx, snap_grid);
+ }
+
+ if (state.has("show_grid")) {
+ show_grid = state["show_grid"];
+ int idx = view_menu->get_popup()->get_item_index(SHOW_GRID);
+ view_menu->get_popup()->set_item_checked(idx, show_grid);
+ }
+
+ if (state.has("show_origin")) {
+ show_origin = state["show_origin"];
+ int idx = view_menu->get_popup()->get_item_index(SHOW_ORIGIN);
+ view_menu->get_popup()->set_item_checked(idx, show_origin);
+ }
+
+ if (state.has("show_viewport")) {
+ show_viewport = state["show_viewport"];
+ int idx = view_menu->get_popup()->get_item_index(SHOW_VIEWPORT);
+ view_menu->get_popup()->set_item_checked(idx, show_viewport);
+ }
+
+ if (state.has("show_rulers")) {
+ show_rulers = state["show_rulers"];
+ int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS);
+ view_menu->get_popup()->set_item_checked(idx, show_rulers);
+ }
+
+ if (state.has("show_guides")) {
+ show_guides = state["show_guides"];
+ int idx = view_menu->get_popup()->get_item_index(SHOW_GUIDES);
+ view_menu->get_popup()->set_item_checked(idx, show_guides);
+ }
+
+ if (state.has("show_helpers")) {
+ show_helpers = state["show_helpers"];
+ int idx = view_menu->get_popup()->get_item_index(SHOW_HELPERS);
+ view_menu->get_popup()->set_item_checked(idx, show_helpers);
+ }
+
+ if (state.has("snap_rotation")) {
+ snap_rotation = state["snap_rotation"];
+ int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
+ snap_config_menu->get_popup()->set_item_checked(idx, snap_rotation);
+ }
+
+ if (state.has("snap_relative")) {
+ snap_relative = state["snap_relative"];
+ int idx = snap_config_menu->get_popup()->get_item_index(SNAP_RELATIVE);
+ snap_config_menu->get_popup()->set_item_checked(idx, snap_relative);
+ }
+
+ if (state.has("snap_pixel")) {
+ snap_pixel = state["snap_pixel"];
+ int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
+ snap_config_menu->get_popup()->set_item_checked(idx, snap_pixel);
+ }
+
+ if (state.has("skeleton_show_bones")) {
+ skeleton_show_bones = state["skeleton_show_bones"];
+ int idx = skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES);
+ skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones);
+ }
+
+ viewport->update();
+}
+
void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) {
hb->add_child(p_control);
}
+void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) {
+
+ hb->remove_child(p_control);
+}
+
HSplitContainer *CanvasItemEditor::get_palette_split() {
return palette_split;
@@ -4018,6 +4150,7 @@ void CanvasItemEditor::focus_selection() {
CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
+ bone_list_dirty = false;
tool = TOOL_SELECT;
undo_redo = p_editor->get_undo_redo();
editor = p_editor;
@@ -4037,21 +4170,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
bottom_split->add_child(palette_split);
palette_split->set_v_size_flags(SIZE_EXPAND_FILL);
- viewport_base = memnew(Control);
- palette_split->add_child(viewport_base);
- viewport_base->set_clip_contents(true);
- viewport_base->connect("draw", this, "_draw_viewport_base");
- viewport_base->connect("gui_input", this, "_gui_input_viewport_base");
- viewport_base->set_focus_mode(FOCUS_ALL);
- viewport_base->set_v_size_flags(SIZE_EXPAND_FILL);
- viewport_base->set_h_size_flags(SIZE_EXPAND_FILL);
-
viewport_scrollable = memnew(Control);
- viewport_base->add_child(viewport_scrollable);
+ palette_split->add_child(viewport_scrollable);
viewport_scrollable->set_mouse_filter(MOUSE_FILTER_PASS);
- viewport_scrollable->set_draw_behind_parent(true);
- viewport_scrollable->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- viewport_scrollable->set_begin(Point2(RULER_WIDTH, RULER_WIDTH));
+ viewport_scrollable->set_clip_contents(true);
+ viewport_scrollable->set_v_size_flags(SIZE_EXPAND_FILL);
+ viewport_scrollable->set_h_size_flags(SIZE_EXPAND_FILL);
+ viewport_scrollable->connect("draw", this, "_update_scrollbars");
ViewportContainer *scene_tree = memnew(ViewportContainer);
viewport_scrollable->add_child(scene_tree);
@@ -4064,46 +4189,48 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->set_mouse_filter(MOUSE_FILTER_PASS);
viewport->set_anchors_and_margins_preset(Control::PRESET_WIDE);
viewport->set_clip_contents(true);
+ viewport->set_focus_mode(FOCUS_ALL);
viewport->connect("draw", this, "_draw_viewport");
viewport->connect("gui_input", this, "_gui_input_viewport");
h_scroll = memnew(HScrollBar);
viewport->add_child(h_scroll);
- h_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED);
+ h_scroll->connect("value_changed", this, "_update_scroll");
h_scroll->hide();
v_scroll = memnew(VScrollBar);
viewport->add_child(v_scroll);
- v_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED);
+ v_scroll->connect("value_changed", this, "_update_scroll");
v_scroll->hide();
- HBoxContainer *zoom_hb = memnew(HBoxContainer);
+ zoom_hb = memnew(HBoxContainer);
viewport->add_child(zoom_hb);
zoom_hb->set_begin(Point2(5, 5));
zoom_minus = memnew(ToolButton);
zoom_hb->add_child(zoom_minus);
- zoom_minus->connect("pressed", this, "_zoom_minus");
+ zoom_minus->connect("pressed", this, "_button_zoom_minus");
+ zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom out"), KEY_MASK_CMD | KEY_MINUS));
zoom_minus->set_focus_mode(FOCUS_NONE);
zoom_reset = memnew(ToolButton);
zoom_hb->add_child(zoom_reset);
- zoom_reset->connect("pressed", this, "_zoom_reset");
+ zoom_reset->connect("pressed", this, "_button_zoom_reset");
+ zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom reset"), KEY_MASK_CMD | KEY_0));
zoom_reset->set_focus_mode(FOCUS_NONE);
zoom_plus = memnew(ToolButton);
zoom_hb->add_child(zoom_plus);
- zoom_plus->connect("pressed", this, "_zoom_plus");
+ zoom_plus->connect("pressed", this, "_button_zoom_plus");
+ zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom in"), KEY_MASK_CMD | KEY_PLUS));
zoom_plus->set_focus_mode(FOCUS_NONE);
updating_scroll = false;
- handle_len = 10;
- first_update = true;
select_button = memnew(ToolButton);
hb->add_child(select_button);
select_button->set_toggle_mode(true);
- select_button->connect("pressed", this, "_tool_select", make_binds(TOOL_SELECT));
+ select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECT));
select_button->set_pressed(true);
select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q));
select_button->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate") + "\n" + TTR("Alt+Drag: Move") + "\n" + TTR("Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving).") + "\n" + TTR("Alt+RMB: Depth list selection"));
@@ -4111,14 +4238,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
move_button = memnew(ToolButton);
hb->add_child(move_button);
move_button->set_toggle_mode(true);
- move_button->connect("pressed", this, "_tool_select", make_binds(TOOL_MOVE));
+ move_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_MOVE));
move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W));
move_button->set_tooltip(TTR("Move Mode"));
rotate_button = memnew(ToolButton);
hb->add_child(rotate_button);
rotate_button->set_toggle_mode(true);
- rotate_button->connect("pressed", this, "_tool_select", make_binds(TOOL_ROTATE));
+ rotate_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_ROTATE));
rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E));
rotate_button->set_tooltip(TTR("Rotate Mode"));
@@ -4127,19 +4254,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
list_select_button = memnew(ToolButton);
hb->add_child(list_select_button);
list_select_button->set_toggle_mode(true);
- list_select_button->connect("pressed", this, "_tool_select", make_binds(TOOL_LIST_SELECT));
+ list_select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_LIST_SELECT));
list_select_button->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode)."));
pivot_button = memnew(ToolButton);
hb->add_child(pivot_button);
pivot_button->set_toggle_mode(true);
- pivot_button->connect("pressed", this, "_tool_select", make_binds(TOOL_EDIT_PIVOT));
+ pivot_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_EDIT_PIVOT));
pivot_button->set_tooltip(TTR("Click to change object's rotation pivot."));
pan_button = memnew(ToolButton);
hb->add_child(pan_button);
pan_button->set_toggle_mode(true);
- pan_button->connect("pressed", this, "_tool_select", make_binds(TOOL_PAN));
+ pan_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PAN));
pan_button->set_tooltip(TTR("Pan Mode"));
hb->add_child(memnew(VSeparator));
@@ -4147,7 +4274,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
snap_button = memnew(ToolButton);
hb->add_child(snap_button);
snap_button->set_toggle_mode(true);
- snap_button->connect("toggled", this, "_toggle_snap");
+ snap_button->connect("toggled", this, "_button_toggle_snap");
snap_button->set_tooltip(TTR("Toggles snapping"));
snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_S));
@@ -4174,6 +4301,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to parent")), SNAP_USE_NODE_PARENT);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_anchors", TTR("Snap to node anchor")), SNAP_USE_NODE_ANCHORS);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_sides", TTR("Snap to node sides")), SNAP_USE_NODE_SIDES);
+ smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_center", TTR("Snap to node center")), SNAP_USE_NODE_CENTER);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to other nodes")), SNAP_USE_OTHER_NODES);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to guides")), SNAP_USE_GUIDES);
@@ -4207,13 +4335,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p = skeleton_menu->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES);
- p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Custom Bone(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Custom Bones")), SKELETON_CLEAR_BONES);
p->connect("id_pressed", this, "_popup_callback");
hb->add_child(memnew(VSeparator));
@@ -4225,9 +4353,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p = view_menu->get_popup();
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid"), KEY_G), SHOW_GRID);
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show helpers"), KEY_H), SHOW_HELPERS);
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show rulers"), KEY_R), SHOW_RULERS);
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show guides"), KEY_Y), SHOW_GUIDES);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers"), KEY_R), SHOW_RULERS);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION);
@@ -4305,9 +4435,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
selection_menu->connect("id_pressed", this, "_selection_result_pressed");
selection_menu->connect("popup_hide", this, "_selection_menu_hide");
- drag_pivot_shortcut = ED_SHORTCUT("canvas_item_editor/drag_pivot", TTR("Drag pivot from mouse position"), KEY_MASK_SHIFT | KEY_V);
- set_pivot_shortcut = ED_SHORTCUT("canvas_item_editor/set_pivot", TTR("Set pivot at mouse position"), KEY_V);
-
multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY);
divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE);
@@ -4315,14 +4442,15 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_rot = true;
key_scale = false;
- edited_guide_pos = Point2();
- edited_guide_index = -1;
-
show_grid = false;
+ show_origin = true;
+ show_viewport = true;
show_helpers = false;
show_rulers = true;
show_guides = true;
zoom = 1;
+ view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH);
+ previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen
grid_offset = Point2();
grid_step = Point2(10, 10);
grid_step_multiplier = 0;
@@ -4332,6 +4460,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
snap_node_parent = true;
snap_node_anchors = true;
snap_node_sides = true;
+ snap_node_center = true;
snap_other_nodes = true;
snap_grid = true;
snap_guides = true;
@@ -4339,17 +4468,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
snap_pixel = false;
skeleton_show_bones = true;
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
- box_selecting = false;
- //zoom=0.5;
singleton = this;
set_process_unhandled_key_input(true);
- can_move_pivot = false;
- drag = DRAG_NONE;
+
+ drag_type = DRAG_NONE;
+ drag_from = Vector2();
+ drag_to = Vector2();
+ dragged_guide_pos = Point2();
+ dragged_guide_index = -1;
+
bone_last_frame = 0;
- additive_selection = false;
- // Update the menus checkboxes
+ // Update the menus' checkboxes
call_deferred("set_state", get_state());
}
@@ -4372,7 +4503,7 @@ void CanvasItemEditorPlugin::make_visible(bool p_visible) {
canvas_item_editor->show();
canvas_item_editor->set_physics_process(true);
VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport_rid(), false);
- canvas_item_editor->viewport_base->grab_focus();
+ canvas_item_editor->viewport->grab_focus();
} else {
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index ace87f9fe2..a1957b892e 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -50,9 +50,6 @@ class CanvasItemEditorSelectedItem : public Object {
GDCLASS(CanvasItemEditorSelectedItem, Object);
public:
- Variant undo_state;
- Vector2 undo_pivot;
-
Transform2D prev_xform;
float prev_rot;
Rect2 prev_rect;
@@ -62,6 +59,11 @@ public:
Transform2D pre_drag_xform;
Rect2 pre_drag_rect;
+ List<float> pre_drag_bones_length;
+ List<Dictionary> pre_drag_bones_undo_state;
+
+ Dictionary undo_state;
+
CanvasItemEditorSelectedItem() { prev_rot = 0; }
};
@@ -86,6 +88,7 @@ class CanvasItemEditor : public VBoxContainer {
SNAP_USE_NODE_PARENT,
SNAP_USE_NODE_ANCHORS,
SNAP_USE_NODE_SIDES,
+ SNAP_USE_NODE_CENTER,
SNAP_USE_OTHER_NODES,
SNAP_USE_GRID,
SNAP_USE_GUIDES,
@@ -97,6 +100,8 @@ class CanvasItemEditor : public VBoxContainer {
SHOW_HELPERS,
SHOW_RULERS,
SHOW_GUIDES,
+ SHOW_ORIGIN,
+ SHOW_VIEWPORT,
LOCK_SELECTED,
UNLOCK_SELECTED,
GROUP_SELECTED,
@@ -169,6 +174,7 @@ class CanvasItemEditor : public VBoxContainer {
enum DragType {
DRAG_NONE,
+ DRAG_BOX_SELECTION,
DRAG_LEFT,
DRAG_TOP_LEFT,
DRAG_TOP,
@@ -185,29 +191,20 @@ class CanvasItemEditor : public VBoxContainer {
DRAG_ALL,
DRAG_ROTATE,
DRAG_PIVOT,
- DRAG_NODE_2D,
DRAG_V_GUIDE,
DRAG_H_GUIDE,
DRAG_DOUBLE_GUIDE,
- };
-
- enum KeyMoveMODE {
- MOVE_VIEW_BASE,
- MOVE_LOCAL_BASE,
- MOVE_LOCAL_WITH_ROT
+ DRAG_KEY_MOVE,
+ DRAG_PAN
};
EditorSelection *editor_selection;
- bool additive_selection;
+ bool selection_menu_additive_selection;
Tool tool;
- bool first_update;
Control *viewport;
- Control *viewport_base;
Control *viewport_scrollable;
- bool can_move_pivot;
-
HScrollBar *h_scroll;
VScrollBar *v_scroll;
HBoxContainer *hb;
@@ -220,8 +217,12 @@ class CanvasItemEditor : public VBoxContainer {
bool show_grid;
bool show_rulers;
bool show_guides;
+ bool show_origin;
+ bool show_viewport;
bool show_helpers;
float zoom;
+ Point2 view_offset;
+ Point2 previous_update_view_offset;
Point2 grid_offset;
Point2 grid_step;
@@ -233,6 +234,7 @@ class CanvasItemEditor : public VBoxContainer {
bool snap_node_parent;
bool snap_node_anchors;
bool snap_node_sides;
+ bool snap_node_center;
bool snap_other_nodes;
bool snap_grid;
bool snap_guides;
@@ -240,14 +242,10 @@ class CanvasItemEditor : public VBoxContainer {
bool snap_relative;
bool snap_pixel;
bool skeleton_show_bones;
- bool box_selecting;
- Point2 box_selecting_to;
bool key_pos;
bool key_rot;
bool key_scale;
- void _tool_select(int p_index);
-
MenuOption last_option;
struct _SelectResult {
@@ -261,39 +259,36 @@ class CanvasItemEditor : public VBoxContainer {
};
Vector<_SelectResult> selection_results;
+ Vector<_SelectResult> hovering_results;
struct BoneList {
Transform2D xform;
- Vector2 from;
- Vector2 to;
- ObjectID bone;
+ float length;
uint64_t last_pass;
};
uint64_t bone_last_frame;
- Map<ObjectID, BoneList> bone_list;
-
- Transform2D bone_orig_xform;
- struct BoneIK {
-
- Variant orig_state;
- Vector2 pos;
- float len;
- Node2D *node;
+ struct BoneKey {
+ ObjectID from;
+ ObjectID to;
+ _FORCE_INLINE_ bool operator<(const BoneKey &p_key) const {
+ if (from == p_key.from)
+ return to < p_key.to;
+ else
+ return from < p_key.from;
+ }
};
- List<BoneIK> bone_ik_list;
+ Map<BoneKey, BoneList> bone_list;
struct PoseClipboard {
-
Vector2 pos;
Vector2 scale;
float rot;
ObjectID id;
};
-
List<PoseClipboard> pose_clipboard;
ToolButton *select_button;
@@ -333,16 +328,17 @@ class CanvasItemEditor : public VBoxContainer {
Control *top_ruler;
Control *left_ruler;
- //PopupMenu *popup;
- DragType drag;
+ DragType drag_type;
Point2 drag_from;
- Point2 drag_point_from;
+ Point2 drag_to;
+ Point2 drag_rotation_center;
+ List<CanvasItem *> drag_selection;
+ int dragged_guide_index;
+ Point2 dragged_guide_pos;
+
bool updating_value_dialog;
- Point2 display_rotate_from;
- Point2 display_rotate_to;
- int edited_guide_index;
- Point2 edited_guide_pos;
+ Point2 box_selecting_to;
Ref<StyleBoxTexture> select_sb;
Ref<Texture> select_handle;
@@ -353,28 +349,21 @@ class CanvasItemEditor : public VBoxContainer {
Ref<ShortCut> multiply_grid_step_shortcut;
Ref<ShortCut> divide_grid_step_shortcut;
- int handle_len;
- bool _is_part_of_subscene(CanvasItem *p_item);
- void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, Vector<_SelectResult> &r_items, int limit = 0);
- void _find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, List<CanvasItem *> *r_items);
+ void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0);
- void _select_click_on_empty_area(Point2 p_click_pos, bool p_append, bool p_box_selection);
- bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append, bool p_drag);
+ void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append);
ConfirmationDialog *snap_dialog;
CanvasItem *ref_item;
- void _edit_set_pivot(const Vector2 &mouse_pos);
void _add_canvas_item(CanvasItem *p_canvas_item);
- void _remove_canvas_item(CanvasItem *p_canvas_item);
- void _clear_canvas_items();
- void _key_move(const Vector2 &p_dir, bool p_snap, KeyMoveMODE p_move_mode);
- void _list_select(const Ref<InputEventMouseButton> &b);
- DragType _get_resize_handle_drag_type(const Point2 &p_click, Vector2 &r_point);
- void _prepare_drag(const Point2 &p_click_pos);
- DragType _get_anchor_handle_drag_type(const Point2 &p_click, Vector2 &r_point);
+ void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false);
+ void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false);
+ void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false);
Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor);
Vector2 _position_to_anchor(const Control *p_control, Vector2 position);
@@ -383,27 +372,21 @@ class CanvasItemEditor : public VBoxContainer {
bool updating_scroll;
void _update_scroll(float);
void _update_scrollbars();
- void _update_cursor();
- void incbeg(float &beg, float &end, float inc, float minsize, bool p_symmetric);
- void incend(float &beg, float &end, float inc, float minsize, bool p_symmetric);
-
void _append_canvas_item(CanvasItem *p_item);
void _snap_changed();
void _selection_result_pressed(int);
void _selection_menu_hide();
UndoRedo *undo_redo;
+ bool _build_bones_list(Node *p_node);
- Point2 _find_topleftmost_point();
-
- void _build_bones_list(Node *p_node);
-
- void _get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform);
+ List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
+ Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
+ void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ Rect2 _get_encompassing_rect(const Node *p_node);
Object *_get_editor_data(Object *p_what);
- CanvasItem *_get_single_item();
- int get_item_count();
void _keying_changed();
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
@@ -411,6 +394,7 @@ class CanvasItemEditor : public VBoxContainer {
void _draw_text_at_position(Point2 p_position, String p_string, Margin p_side);
void _draw_margin_at_position(int p_value, Point2 p_position, Margin p_side);
void _draw_percentage_at_position(float p_value, Point2 p_position, Margin p_side);
+ void _draw_straight_line(Point2 p_from, Point2 p_to, Color p_color);
void _draw_rulers();
void _draw_guides();
@@ -419,16 +403,29 @@ class CanvasItemEditor : public VBoxContainer {
void _draw_selection();
void _draw_axis();
void _draw_bones();
- void _draw_locks_and_groups(Node *p_node, const Transform2D &p_xform);
+ void _draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _draw_hover();
void _draw_viewport();
- void _draw_viewport_base();
+
+ bool _gui_input_anchors(const Ref<InputEvent> &p_event);
+ bool _gui_input_move(const Ref<InputEvent> &p_event);
+ bool _gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event);
+ bool _gui_input_pivot(const Ref<InputEvent> &p_event);
+ bool _gui_input_resize(const Ref<InputEvent> &p_event);
+ bool _gui_input_rotate(const Ref<InputEvent> &p_event);
+ bool _gui_input_select(const Ref<InputEvent> &p_event);
+ bool _gui_input_zoom_or_pan(const Ref<InputEvent> &p_event);
+ bool _gui_input_rulers_and_guides(const Ref<InputEvent> &p_event);
+ bool _gui_input_hover(const Ref<InputEvent> &p_event);
void _gui_input_viewport(const Ref<InputEvent> &p_event);
- void _gui_input_viewport_base(const Ref<InputEvent> &p_event);
void _focus_selection(int p_op);
+ void _solve_IK(Node2D *leaf_node, Point2 target_position);
+
void _snap_if_closer_float(float p_value, float p_target_snap, float &r_current_snap, bool &r_snapped, float p_radius = 10.0);
void _snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation = 0.0, float p_radius = 10.0);
void _snap_other_nodes(Point2 p_value, Point2 &r_current_snap, bool (&r_snapped)[2], const Node *p_current, const CanvasItem *p_to_snap = NULL);
@@ -437,16 +434,22 @@ class CanvasItemEditor : public VBoxContainer {
void _set_margins_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_margins_preset(Control::LayoutPreset p_preset);
+ HBoxContainer *zoom_hb;
void _zoom_on_position(float p_zoom, Point2 p_position = Point2());
- void _zoom_minus();
- void _zoom_reset();
- void _zoom_plus();
-
- void _toggle_snap(bool p_status);
+ void _button_zoom_minus();
+ void _button_zoom_reset();
+ void _button_zoom_plus();
+ void _button_toggle_snap(bool p_status);
+ void _button_tool_select(int p_index);
HSplitContainer *palette_split;
VSplitContainer *bottom_split;
+ bool bone_list_dirty;
+ void _queue_update_bone_list();
+ void _update_bone_list();
+ void _tree_changed(Node *);
+
friend class CanvasItemEditorPlugin;
protected:
@@ -494,9 +497,10 @@ public:
SNAP_NODE_PARENT = 1 << 3,
SNAP_NODE_ANCHORS = 1 << 4,
SNAP_NODE_SIDES = 1 << 5,
- SNAP_OTHER_NODES = 1 << 6,
+ SNAP_NODE_CENTER = 1 << 6,
+ SNAP_OTHER_NODES = 1 << 7,
- SNAP_DEFAULT = 0x07,
+ SNAP_DEFAULT = SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL,
};
Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, const CanvasItem *p_canvas_item = NULL, unsigned int p_forced_modes = 0);
@@ -509,6 +513,7 @@ public:
void set_state(const Dictionary &p_state);
void add_control_to_menu_panel(Control *p_control);
+ void remove_control_from_menu_panel(Control *p_control);
HSplitContainer *get_palette_split();
VSplitContainer *get_bottom_split();
diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp
index d8d3c0cee6..e837359d0c 100644
--- a/editor/plugins/collision_polygon_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -33,10 +33,12 @@
#include "canvas_item_editor_plugin.h"
#include "editor/editor_settings.h"
#include "os/file_access.h"
+#include "os/input.h"
+#include "os/keyboard.h"
#include "scene/3d/camera.h"
#include "spatial_editor_plugin.h"
-void CollisionPolygonEditor::_notification(int p_what) {
+void Polygon3DEditor::_notification(int p_what) {
switch (p_what) {
@@ -53,15 +55,15 @@ void CollisionPolygonEditor::_notification(int p_what) {
return;
}
- if (node->get_depth() != prev_depth) {
+ if (_get_depth() != prev_depth) {
_polygon_draw();
- prev_depth = node->get_depth();
+ prev_depth = _get_depth();
}
} break;
}
}
-void CollisionPolygonEditor::_node_removed(Node *p_node) {
+void Polygon3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = NULL;
@@ -72,7 +74,7 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) {
}
}
-void CollisionPolygonEditor::_menu_option(int p_option) {
+void Polygon3DEditor::_menu_option(int p_option) {
switch (p_option) {
@@ -91,10 +93,10 @@ void CollisionPolygonEditor::_menu_option(int p_option) {
}
}
-void CollisionPolygonEditor::_wip_close() {
+void Polygon3DEditor::_wip_close() {
undo_redo->create_action(TTR("Create Poly3D"));
- undo_redo->add_undo_method(node, "set_polygon", node->get_polygon());
+ undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon"));
undo_redo->add_do_method(node, "set_polygon", wip);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
@@ -107,14 +109,14 @@ void CollisionPolygonEditor::_wip_close() {
undo_redo->commit_action();
}
-bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
+bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
if (!node)
return false;
Transform gt = node->get_global_transform();
Transform gi = gt.affine_inverse();
- float depth = node->get_depth() * 0.5;
+ float depth = _get_depth() * 0.5;
Vector3 n = gt.basis.get_axis(2).normalized();
Plane p(gt.origin + n * depth, n);
@@ -135,12 +137,14 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Vector2 cpoint(spoint.x, spoint.y);
- cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ //DO NOT snap here, it's confusing in 3D for adding points.
+ //Let the snap happen when the point is being moved, instead.
+ //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
- Vector<Vector2> poly = node->get_polygon();
+ Vector<Vector2> poly = node->call("get_polygon");
//first check if a point is to be added (segment split)
- real_t grab_treshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
+ real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
switch (mode) {
@@ -154,12 +158,13 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
wip.push_back(cpoint);
wip_active = true;
edited_point_pos = cpoint;
+ snap_ignore = false;
_polygon_draw();
edited_point = 1;
return true;
} else {
- if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_treshold) {
+ if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
@@ -168,6 +173,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
wip.push_back(cpoint);
edited_point = wip.size();
+ snap_ignore = false;
_polygon_draw();
return true;
}
@@ -213,7 +219,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
continue; //not valid to reuse point
real_t d = cp.distance_to(gpoint);
- if (d < closest_dist && d < grab_treshold) {
+ if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
@@ -226,8 +232,10 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
poly.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
- node->set_polygon(poly);
+ node->call("set_polygon", poly);
_polygon_draw();
+ snap_ignore = true;
+
return true;
}
} else {
@@ -242,7 +250,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
real_t d = cp.distance_to(gpoint);
- if (d < closest_dist && d < grab_treshold) {
+ if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
@@ -255,11 +263,14 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
edited_point = closest_idx;
edited_point_pos = poly[closest_idx];
_polygon_draw();
+ snap_ignore = false;
return true;
}
}
} else {
+ snap_ignore = false;
+
if (edited_point != -1) {
//apply
@@ -288,7 +299,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
real_t d = cp.distance_to(gpoint);
- if (d < closest_dist && d < grab_treshold) {
+ if (d < closest_dist && d < grab_threshold) {
closest_dist = d;
closest_pos = cp;
closest_idx = i;
@@ -315,7 +326,6 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
-
if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) {
Vector2 gpoint = mm->get_position();
@@ -332,7 +342,13 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Vector2 cpoint(spoint.x, spoint.y);
- cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ snap_ignore = false;
+ }
+
+ if (!snap_ignore) {
+ cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ }
edited_point_pos = cpoint;
_polygon_draw();
@@ -341,7 +357,16 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
return false;
}
-void CollisionPolygonEditor::_polygon_draw() {
+
+float Polygon3DEditor::_get_depth() {
+
+ if (bool(node->call("_has_editable_3d_polygon_no_depth")))
+ return 0;
+
+ return float(node->call("get_depth"));
+}
+
+void Polygon3DEditor::_polygon_draw() {
if (!node)
return;
@@ -351,9 +376,9 @@ void CollisionPolygonEditor::_polygon_draw() {
if (wip_active)
poly = wip;
else
- poly = node->get_polygon();
+ poly = node->call("get_polygon");
- float depth = node->get_depth() * 0.5;
+ float depth = _get_depth() * 0.5;
imgeom->clear();
imgeom->set_material_override(line_material);
@@ -464,13 +489,13 @@ void CollisionPolygonEditor::_polygon_draw() {
m->surface_set_material(0, handle_material);
}
-void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
+void Polygon3DEditor::edit(Node *p_collision_polygon) {
if (p_collision_polygon) {
- node = Object::cast_to<CollisionPolygon>(p_collision_polygon);
+ node = Object::cast_to<Spatial>(p_collision_polygon);
//Enable the pencil tool if the polygon is empty
- if (node->get_polygon().size() == 0) {
+ if (Vector<Vector2>(node->call("get_polygon")).size() == 0) {
_menu_option(MODE_CREATE);
}
wip.clear();
@@ -491,14 +516,14 @@ void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
}
}
-void CollisionPolygonEditor::_bind_methods() {
+void Polygon3DEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_menu_option"), &CollisionPolygonEditor::_menu_option);
- ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygonEditor::_polygon_draw);
- ClassDB::bind_method(D_METHOD("_node_removed"), &CollisionPolygonEditor::_node_removed);
+ ClassDB::bind_method(D_METHOD("_menu_option"), &Polygon3DEditor::_menu_option);
+ ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);
+ ClassDB::bind_method(D_METHOD("_node_removed"), &Polygon3DEditor::_node_removed);
}
-CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
+Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) {
node = NULL;
editor = p_editor;
@@ -543,24 +568,26 @@ CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
m.instance();
pointsm->set_mesh(m);
pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
+
+ snap_ignore = false;
}
-CollisionPolygonEditor::~CollisionPolygonEditor() {
+Polygon3DEditor::~Polygon3DEditor() {
memdelete(imgeom);
}
-void CollisionPolygonEditorPlugin::edit(Object *p_object) {
+void Polygon3DEditorPlugin::edit(Object *p_object) {
collision_polygon_editor->edit(Object::cast_to<Node>(p_object));
}
-bool CollisionPolygonEditorPlugin::handles(Object *p_object) const {
+bool Polygon3DEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("CollisionPolygon");
+ return Object::cast_to<Spatial>(p_object) && bool(p_object->call("_is_editable_3d_polygon"));
}
-void CollisionPolygonEditorPlugin::make_visible(bool p_visible) {
+void Polygon3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
collision_polygon_editor->show();
@@ -571,14 +598,14 @@ void CollisionPolygonEditorPlugin::make_visible(bool p_visible) {
}
}
-CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) {
+Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- collision_polygon_editor = memnew(CollisionPolygonEditor(p_node));
+ collision_polygon_editor = memnew(Polygon3DEditor(p_node));
SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
collision_polygon_editor->hide();
}
-CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin() {
+Polygon3DEditorPlugin::~Polygon3DEditorPlugin() {
}
diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h
index f1f215b092..4229808e2f 100644
--- a/editor/plugins/collision_polygon_editor_plugin.h
+++ b/editor/plugins/collision_polygon_editor_plugin.h
@@ -44,9 +44,9 @@
class CanvasItemEditor;
-class CollisionPolygonEditor : public HBoxContainer {
+class Polygon3DEditor : public HBoxContainer {
- GDCLASS(CollisionPolygonEditor, HBoxContainer);
+ GDCLASS(Polygon3DEditor, HBoxContainer);
UndoRedo *undo_redo;
enum Mode {
@@ -66,7 +66,7 @@ class CollisionPolygonEditor : public HBoxContainer {
EditorNode *editor;
Panel *panel;
- CollisionPolygon *node;
+ Spatial *node;
ImmediateGeometry *imgeom;
MeshInstance *pointsm;
Ref<ArrayMesh> m;
@@ -78,6 +78,7 @@ class CollisionPolygonEditor : public HBoxContainer {
Vector<Vector2> pre_move_edit;
Vector<Vector2> wip;
bool wip_active;
+ bool snap_ignore;
float prev_depth;
@@ -85,6 +86,8 @@ class CollisionPolygonEditor : public HBoxContainer {
void _polygon_draw();
void _menu_option(int p_option);
+ float _get_depth();
+
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
@@ -93,28 +96,28 @@ protected:
public:
virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
- CollisionPolygonEditor(EditorNode *p_editor);
- ~CollisionPolygonEditor();
+ Polygon3DEditor(EditorNode *p_editor);
+ ~Polygon3DEditor();
};
-class CollisionPolygonEditorPlugin : public EditorPlugin {
+class Polygon3DEditorPlugin : public EditorPlugin {
- GDCLASS(CollisionPolygonEditorPlugin, EditorPlugin);
+ GDCLASS(Polygon3DEditorPlugin, EditorPlugin);
- CollisionPolygonEditor *collision_polygon_editor;
+ Polygon3DEditor *collision_polygon_editor;
EditorNode *editor;
public:
virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
- virtual String get_name() const { return "CollisionPolygon"; }
+ virtual String get_name() const { return "Polygon3DEditor"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
- CollisionPolygonEditorPlugin(EditorNode *p_node);
- ~CollisionPolygonEditorPlugin();
+ Polygon3DEditorPlugin(EditorNode *p_node);
+ ~Polygon3DEditorPlugin();
};
#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 215964235e..8542296bde 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -30,6 +30,7 @@
#include "editor_preview_plugins.h"
+#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "io/file_access_memory.h"
@@ -39,6 +40,38 @@
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
+void post_process_preview(Ref<Image> p_image) {
+
+ if (p_image->get_format() != Image::FORMAT_RGBA8)
+ p_image->convert(Image::FORMAT_RGBA8);
+
+ p_image->lock();
+
+ const int w = p_image->get_width();
+ const int h = p_image->get_height();
+
+ const int r = MIN(w, h) / 32;
+ const int r2 = r * r;
+ Color transparent = Color(0, 0, 0, 0);
+
+ for (int i = 0; i < r; i++) {
+ for (int j = 0; j < r; j++) {
+ int dx = i - r;
+ int dy = j - r;
+ if (dx * dx + dy * dy > r2) {
+ p_image->set_pixel(i, j, transparent);
+ p_image->set_pixel(w - 1 - i, j, transparent);
+ p_image->set_pixel(w - 1 - i, h - 1 - j, transparent);
+ p_image->set_pixel(i, h - 1 - j, transparent);
+ } else {
+ break;
+ }
+ }
+ }
+
+ p_image->unlock();
+}
+
bool EditorTexturePreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Texture");
@@ -90,6 +123,7 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) {
}
img->resize(width, height);
+ post_process_preview(img);
Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
@@ -162,6 +196,7 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) {
}
img->resize(width, height);
+ post_process_preview(img);
Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
@@ -203,6 +238,7 @@ Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_
Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
+ post_process_preview(img);
ptex->create_from_image(img, 0);
return ptex;
@@ -258,6 +294,7 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) {
thumbnail_size *= EDSCALE;
img->convert(Image::FORMAT_RGBA8);
img->resize(thumbnail_size, thumbnail_size);
+ post_process_preview(img);
Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
ptex->create_from_image(img, 0);
return ptex;
@@ -426,19 +463,36 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) {
img->create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8);
Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color");
- bg_color.a = 1.0;
Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color");
Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color");
Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color");
+ if (EditorSettings::get_singleton()->get("text_editor/theme/color_theme") == "Adaptive") {
+ Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
+
+ bg_color = tm->get_color("text_editor/highlighting/background_color", "Editor");
+ keyword_color = tm->get_color("text_editor/highlighting/keyword_color", "Editor");
+ text_color = tm->get_color("text_editor/highlighting/text_color", "Editor");
+ symbol_color = tm->get_color("text_editor/highlighting/symbol_color", "Editor");
+ }
+
img->lock();
+ if (bg_color.a == 0)
+ bg_color = Color(0, 0, 0, 0);
+ bg_color.a = MAX(bg_color.a, 0.2); // some background
+
for (int i = 0; i < thumbnail_size; i++) {
for (int j = 0; j < thumbnail_size; j++) {
img->set_pixel(i, j, bg_color);
}
}
+ const int x0 = thumbnail_size / 8;
+ const int y0 = thumbnail_size / 8;
+ const int available_height = thumbnail_size - 2 * y0;
+ col = x0;
+
bool prev_is_text = false;
bool in_keyword = false;
for (int i = 0; i < code.length(); i++) {
@@ -471,8 +525,8 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) {
Color ul = color;
ul.a *= 0.5;
- img->set_pixel(col, line * 2, bg_color.blend(ul));
- img->set_pixel(col, line * 2 + 1, color);
+ img->set_pixel(col, y0 + line * 2, bg_color.blend(ul));
+ img->set_pixel(col, y0 + line * 2 + 1, color);
prev_is_text = _is_text_char(c);
}
@@ -482,9 +536,9 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) {
in_keyword = false;
if (c == '\n') {
- col = 0;
+ col = x0;
line++;
- if (line >= thumbnail_size / 2)
+ if (line >= available_height / 2)
break;
} else if (c == '\t') {
col += 3;
@@ -495,6 +549,8 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) {
img->unlock();
+ post_process_preview(img);
+
Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
ptex->create_from_image(img, 0);
@@ -762,6 +818,7 @@ Ref<Texture> EditorSamplePreviewPlugin::generate(const RES& p_from) {
}
imgdata = PoolVector<uint8_t>::Write();
+ post_process_preview(img);
Ref<ImageTexture> ptex = Ref<ImageTexture>( memnew( ImageTexture));
ptex->create_from_image(Image(w,h,0,Image::FORMAT_RGB8,img),0);
@@ -831,6 +888,7 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) {
thumbnail_size *= EDSCALE;
img->convert(Image::FORMAT_RGBA8);
img->resize(thumbnail_size, thumbnail_size);
+ post_process_preview(img);
Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
ptex->create_from_image(img, 0);
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index 2e12515e30..35b5c3a5f0 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -33,6 +33,8 @@
#include "editor/editor_resource_preview.h"
+void post_process_preview(Ref<Image> p_image);
+
class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator {
GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator)
public:
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index fd5a1f185f..8b44f672b0 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -42,9 +42,18 @@ bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) {
set_item_text(idx, p_value);
else if (what == "icon")
set_item_icon(idx, p_value);
- else if (what == "checkable")
- set_item_checkable(idx, p_value);
- else if (what == "checked")
+ else if (what == "checkable") {
+ // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
+ switch ((int)p_value) {
+ case 0:
+ case 1:
+ set_item_checkable(idx, p_value);
+ break;
+ case 2:
+ set_item_radio_checkable(idx, true);
+ break;
+ }
+ } else if (what == "checked")
set_item_checked(idx, p_value);
else if (what == "id")
set_item_id(idx, p_value);
@@ -68,9 +77,14 @@ bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_text(idx);
else if (what == "icon")
r_ret = get_item_icon(idx);
- else if (what == "checkable")
- r_ret = is_item_checkable(idx);
- else if (what == "checked")
+ else if (what == "checkable") {
+ // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
+ if (!is_item_checkable(idx)) {
+ r_ret = 0;
+ } else {
+ r_ret = is_item_radio_checkable(idx) ? 2 : 1;
+ }
+ } else if (what == "checked")
r_ret = is_item_checked(idx);
else if (what == "id")
r_ret = get_item_id(idx);
@@ -95,7 +109,7 @@ void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const {
int flags = get_flags();
if (flags & FLAG_CHECKABLE) {
- p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"));
p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked"));
}
@@ -247,7 +261,7 @@ void ItemListEditor::_node_removed(Node *p_node) {
void ItemListEditor::_notification(int p_notification) {
- if (p_notification == NOTIFICATION_ENTER_TREE) {
+ if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) {
add_button->set_icon(get_icon("Add", "EditorIcons"));
del_button->set_icon(get_icon("Remove", "EditorIcons"));
diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h
index bd7d3db10d..d6a071b9b9 100644
--- a/editor/plugins/item_list_editor_plugin.h
+++ b/editor/plugins/item_list_editor_plugin.h
@@ -74,7 +74,9 @@ public:
virtual Ref<Texture> get_item_icon(int p_idx) const { return Ref<Texture>(); };
virtual void set_item_checkable(int p_idx, bool p_check) {}
+ virtual void set_item_radio_checkable(int p_idx, bool p_check) {}
virtual bool is_item_checkable(int p_idx) const { return false; };
+ virtual bool is_item_radio_checkable(int p_idx) const { return false; };
virtual void set_item_checked(int p_idx, bool p_checked) {}
virtual bool is_item_checked(int p_idx) const { return false; };
@@ -145,7 +147,9 @@ public:
virtual Ref<Texture> get_item_icon(int p_idx) const { return pp->get_item_icon(p_idx); }
virtual void set_item_checkable(int p_idx, bool p_check) { pp->set_item_as_checkable(p_idx, p_check); }
+ virtual void set_item_radio_checkable(int p_idx, bool p_check) { pp->set_item_as_radio_checkable(p_idx, p_check); }
virtual bool is_item_checkable(int p_idx) const { return pp->is_item_checkable(p_idx); }
+ virtual bool is_item_radio_checkable(int p_idx) const { return pp->is_item_radio_checkable(p_idx); }
virtual void set_item_checked(int p_idx, bool p_checked) { pp->set_item_checked(p_idx, p_checked); }
virtual bool is_item_checked(int p_idx) const { return pp->is_item_checked(p_idx); }
diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp
index cb5f7ba76c..74b6f14c2e 100644
--- a/editor/plugins/mesh_instance_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_editor_plugin.cpp
@@ -344,6 +344,10 @@ void MeshInstanceEditor::_create_outline_mesh() {
err_dialog->set_text(TTR("Mesh has not surface to create outlines from!"));
err_dialog->popup_centered_minsize();
return;
+ } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) {
+ err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!"));
+ err_dialog->popup_centered_minsize();
+ return;
}
Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value());
@@ -396,7 +400,7 @@ MeshInstanceEditor::MeshInstanceEditor() {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Create Outline Mesh.."), MENU_OPTION_CREATE_OUTLINE_MESH);
+ options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
diff --git a/editor/plugins/navigation_mesh_generator.cpp b/editor/plugins/navigation_mesh_generator.cpp
index 5d4e5520f4..0537c5c31f 100644
--- a/editor/plugins/navigation_mesh_generator.cpp
+++ b/editor/plugins/navigation_mesh_generator.cpp
@@ -280,26 +280,20 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
_build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices);
- if (hf) {
- rcFreeHeightField(hf);
- hf = 0;
- }
- if (chf) {
- rcFreeCompactHeightfield(chf);
- chf = 0;
- }
- if (cset) {
- rcFreeContourSet(cset);
- cset = 0;
- }
- if (poly_mesh) {
- rcFreePolyMesh(poly_mesh);
- poly_mesh = 0;
- }
- if (detail_mesh) {
- rcFreePolyMeshDetail(detail_mesh);
- detail_mesh = 0;
- }
+ rcFreeHeightField(hf);
+ hf = 0;
+
+ rcFreeCompactHeightfield(chf);
+ chf = 0;
+
+ rcFreeContourSet(cset);
+ cset = 0;
+
+ rcFreePolyMesh(poly_mesh);
+ poly_mesh = 0;
+
+ rcFreePolyMeshDetail(detail_mesh);
+ detail_mesh = 0;
}
ep.step(TTR("Done!"), 11);
}
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
index abee9a9893..6d11079759 100644
--- a/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -94,7 +94,7 @@ void Particles2DEditorPlugin::_generate_visibility_rect() {
while (running < time) {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- ep.step("Generating..", int(running), true);
+ ep.step("Generating...", int(running), true);
OS::get_singleton()->delay_usec(1000);
Rect2 capture = particles->capture_rect();
@@ -229,7 +229,7 @@ void Particles2DEditorPlugin::_generate_emission_mask() {
valid_normals.resize(vpc);
}
- ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image.."));
+ ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image..."));
ERR_FAIL_COND(valid_positions.size() == 0);
PoolVector<uint8_t> texdata;
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index d129b6c5c3..7728995a99 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -129,7 +129,7 @@ void ParticlesEditor::_generate_aabb() {
while (running < time) {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- ep.step("Generating..", int(running), true);
+ ep.step("Generating...", int(running), true);
OS::get_singleton()->delay_usec(1000);
AABB capture = node->capture_aabb();
diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
index 056219a575..6dde639c54 100644
--- a/editor/plugins/path_editor_plugin.cpp
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -167,18 +167,12 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
Vector3 ofs;
- if (p_cancel) {
-
- return;
- }
-
if (t == 0) {
-
if (p_cancel) {
-
c->set_point_in(p_idx, p_restore);
return;
}
+
ur->create_action(TTR("Set Curve In Position"));
ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx));
ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore);
@@ -186,10 +180,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
} else {
if (p_cancel) {
-
c->set_point_out(idx, p_restore);
+
return;
}
+
ur->create_action(TTR("Set Curve Out Position"));
ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));
ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore);
diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp
new file mode 100644
index 0000000000..42f1adcadf
--- /dev/null
+++ b/editor/plugins/physical_bone_plugin.cpp
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* physical_bone_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "physical_bone_plugin.h"
+#include "editor/plugins/spatial_editor_plugin.h"
+#include "scene/3d/physics_body.h"
+
+void PhysicalBoneEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_on_toggle_button_transform_joint", "is_pressed"), &PhysicalBoneEditor::_on_toggle_button_transform_joint);
+}
+
+void PhysicalBoneEditor::_on_toggle_button_transform_joint(bool p_is_pressed) {
+
+ _set_move_joint();
+}
+
+void PhysicalBoneEditor::_set_move_joint() {
+ if (selected) {
+ selected->_set_gizmo_move_joint(button_transform_joint->is_pressed());
+ }
+}
+
+PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) :
+ editor(p_editor),
+ selected(NULL) {
+
+ spatial_editor_hb = memnew(HBoxContainer);
+ spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
+
+ spatial_editor_hb->add_child(memnew(VSeparator));
+
+ button_transform_joint = memnew(ToolButton);
+ spatial_editor_hb->add_child(button_transform_joint);
+
+ button_transform_joint->set_text(TTR("Move joint"));
+ button_transform_joint->set_icon(SpatialEditor::get_singleton()->get_icon("PhysicalBone", "EditorIcons"));
+ button_transform_joint->set_toggle_mode(true);
+ button_transform_joint->connect("toggled", this, "_on_toggle_button_transform_joint");
+
+ hide();
+}
+
+PhysicalBoneEditor::~PhysicalBoneEditor() {
+ // TODO the spatial_editor_hb should be removed from SpatialEditor, but in this moment it's not possible
+ for (int i = spatial_editor_hb->get_child_count() - 1; 0 <= i; --i) {
+ Node *n = spatial_editor_hb->get_child(i);
+ spatial_editor_hb->remove_child(n);
+ memdelete(n);
+ }
+ memdelete(spatial_editor_hb);
+}
+
+void PhysicalBoneEditor::set_selected(PhysicalBone *p_pb) {
+
+ button_transform_joint->set_pressed(false);
+
+ _set_move_joint();
+ selected = p_pb;
+ _set_move_joint();
+}
+
+void PhysicalBoneEditor::hide() {
+ spatial_editor_hb->hide();
+}
+
+void PhysicalBoneEditor::show() {
+ spatial_editor_hb->show();
+}
+
+PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) :
+ editor(p_editor),
+ selected(NULL) {
+
+ physical_bone_editor = memnew(PhysicalBoneEditor(editor));
+}
+
+void PhysicalBonePlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+
+ physical_bone_editor->show();
+ } else {
+
+ physical_bone_editor->hide();
+ physical_bone_editor->set_selected(NULL);
+ selected = NULL;
+ }
+}
+
+void PhysicalBonePlugin::edit(Object *p_node) {
+ selected = static_cast<PhysicalBone *>(p_node); // Trust it
+ ERR_FAIL_COND(!selected);
+
+ physical_bone_editor->set_selected(selected);
+}
diff --git a/editor/plugins/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h
new file mode 100644
index 0000000000..9e7a50307a
--- /dev/null
+++ b/editor/plugins/physical_bone_plugin.h
@@ -0,0 +1,78 @@
+/*************************************************************************/
+/* physical_bone_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PHYSICAL_BONE_PLUGIN_H
+#define PHYSICAL_BONE_PLUGIN_H
+
+#include "editor/editor_node.h"
+
+class PhysicalBoneEditor : public Object {
+ GDCLASS(PhysicalBoneEditor, Object);
+
+ EditorNode *editor;
+ HBoxContainer *spatial_editor_hb;
+ ToolButton *button_transform_joint;
+
+ PhysicalBone *selected;
+
+protected:
+ static void _bind_methods();
+
+private:
+ void _on_toggle_button_transform_joint(bool p_is_pressed);
+ void _set_move_joint();
+
+public:
+ PhysicalBoneEditor(EditorNode *p_editor);
+ ~PhysicalBoneEditor();
+
+ void set_selected(PhysicalBone *p_pb);
+
+ void hide();
+ void show();
+};
+
+class PhysicalBonePlugin : public EditorPlugin {
+ GDCLASS(PhysicalBonePlugin, EditorPlugin);
+
+ EditorNode *editor;
+ PhysicalBone *selected;
+ PhysicalBoneEditor *physical_bone_editor;
+
+public:
+ virtual String get_name() const { return "PhysicalBone"; }
+ virtual bool handles(Object *p_object) const { return p_object->is_class("PhysicalBone"); }
+ virtual void make_visible(bool p_visible);
+ virtual void edit(Object *p_node);
+
+ PhysicalBonePlugin(EditorNode *p_editor);
+};
+
+#endif
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 1db6621718..f04e0a801c 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -35,7 +35,7 @@
#include "os/file_access.h"
#include "os/input.h"
#include "os/keyboard.h"
-
+#include "scene/2d/skeleton_2d.h"
Node2D *Polygon2DEditor::_get_node() const {
return node;
@@ -59,10 +59,15 @@ void Polygon2DEditor::_notification(int p_what) {
button_uv->set_icon(get_icon("Uv", "EditorIcons"));
+ uv_button[UV_MODE_CREATE]->set_icon(get_icon("Add", "EditorIcons"));
uv_button[UV_MODE_EDIT_POINT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
uv_button[UV_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons"));
uv_button[UV_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons"));
uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons"));
+ uv_button[UV_MODE_ADD_SPLIT]->set_icon(get_icon("AddSplit", "EditorIcons"));
+ uv_button[UV_MODE_REMOVE_SPLIT]->set_icon(get_icon("DeleteSplit", "EditorIcons"));
+ uv_button[UV_MODE_PAINT_WEIGHT]->set_icon(get_icon("PaintVertex", "EditorIcons"));
+ uv_button[UV_MODE_CLEAR_WEIGHT]->set_icon(get_icon("UnpaintVertex", "EditorIcons"));
b_snap_grid->set_icon(get_icon("Grid", "EditorIcons"));
b_snap_enable->set_icon(get_icon("SnapGrid", "EditorIcons"));
@@ -75,6 +80,172 @@ void Polygon2DEditor::_notification(int p_what) {
}
}
+void Polygon2DEditor::_sync_bones() {
+
+ print_line("syncinc");
+ if (!node->has_node(node->get_skeleton())) {
+ error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node"));
+ error->popup_centered_minsize();
+ return;
+ }
+
+ Node *sn = node->get_node(node->get_skeleton());
+ Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(sn);
+
+ if (!skeleton) {
+ error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node"));
+ error->popup_centered_minsize();
+ return;
+ }
+
+ Array prev_bones = node->call("_get_bones");
+ node->clear_bones();
+
+ print_line("bones in skeleton: " + itos(skeleton->get_bone_count()));
+
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ NodePath path = skeleton->get_path_to(skeleton->get_bone(i));
+ PoolVector<float> weights;
+ int wc = node->get_polygon().size();
+
+ for (int j = 0; j < prev_bones.size(); j += 2) {
+ NodePath pvp = prev_bones[j];
+ PoolVector<float> pv = prev_bones[j + 1];
+ if (pvp == path && pv.size() == wc) {
+ weights = pv;
+ }
+ }
+
+ if (weights.size() == 0) { //create them
+ weights.resize(node->get_polygon().size());
+ PoolVector<float>::Write w = weights.write();
+ for (int j = 0; j < wc; j++) {
+ w[j] = 0.0;
+ }
+ }
+
+ node->add_bone(path, weights);
+ }
+ Array new_bones = node->call("_get_bones");
+
+ undo_redo->create_action(TTR("Sync bones"));
+ undo_redo->add_do_method(node, "_set_bones", new_bones);
+ undo_redo->add_undo_method(node, "_set_bones", prev_bones);
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->add_do_method(this, "_update_bone_list");
+ undo_redo->add_undo_method(this, "_update_bone_list");
+ undo_redo->commit_action();
+}
+
+void Polygon2DEditor::_update_bone_list() {
+
+ NodePath selected;
+ while (bone_scroll_vb->get_child_count()) {
+ CheckBox *cb = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(0));
+ if (cb && cb->is_pressed()) {
+ selected = cb->get_meta("bone_path");
+ }
+ memdelete(bone_scroll_vb->get_child(0));
+ }
+
+ Ref<ButtonGroup> bg;
+ bg.instance();
+ for (int i = 0; i < node->get_bone_count(); i++) {
+ CheckBox *cb = memnew(CheckBox);
+ NodePath np = node->get_bone_path(i);
+ String name;
+ if (np.get_name_count()) {
+ name = np.get_name(np.get_name_count() - 1);
+ }
+ if (name == String()) {
+ name = "Bone " + itos(i);
+ }
+ cb->set_text(name);
+ cb->set_button_group(bg);
+ cb->set_meta("bone_path", np);
+ bone_scroll_vb->add_child(cb);
+
+ if (np == selected)
+ cb->set_pressed(true);
+
+ cb->connect("pressed", this, "_bone_paint_selected", varray(i));
+ }
+
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_bone_paint_selected(int p_index) {
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_uv_edit_mode_select(int p_mode) {
+
+ if (p_mode == 0) { //uv
+ uv_button[UV_MODE_CREATE]->hide();
+ for (int i = UV_MODE_MOVE; i <= UV_MODE_SCALE; i++) {
+ uv_button[i]->show();
+ }
+ uv_button[UV_MODE_ADD_SPLIT]->hide();
+ uv_button[UV_MODE_REMOVE_SPLIT]->hide();
+ uv_button[UV_MODE_PAINT_WEIGHT]->hide();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
+ _uv_mode(UV_MODE_EDIT_POINT);
+
+ bone_scroll_main_vb->hide();
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+
+ } else if (p_mode == 1) { //poly
+ for (int i = 0; i <= UV_MODE_SCALE; i++) {
+ uv_button[i]->show();
+ }
+ uv_button[UV_MODE_ADD_SPLIT]->hide();
+ uv_button[UV_MODE_REMOVE_SPLIT]->hide();
+ uv_button[UV_MODE_PAINT_WEIGHT]->hide();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
+ _uv_mode(UV_MODE_EDIT_POINT);
+
+ bone_scroll_main_vb->hide();
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+
+ } else if (p_mode == 2) { //splits
+ for (int i = 0; i <= UV_MODE_SCALE; i++) {
+ uv_button[i]->hide();
+ }
+ uv_button[UV_MODE_ADD_SPLIT]->show();
+ uv_button[UV_MODE_REMOVE_SPLIT]->show();
+ uv_button[UV_MODE_PAINT_WEIGHT]->hide();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
+ _uv_mode(UV_MODE_ADD_SPLIT);
+
+ bone_scroll_main_vb->hide();
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+
+ } else if (p_mode == 3) { //bones´
+ for (int i = 0; i <= UV_MODE_REMOVE_SPLIT; i++) {
+ uv_button[i]->hide();
+ }
+ uv_button[UV_MODE_PAINT_WEIGHT]->show();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->show();
+ _uv_mode(UV_MODE_PAINT_WEIGHT);
+
+ bone_scroll_main_vb->show();
+ bone_paint_strength->show();
+ bone_paint_radius->show();
+ bone_paint_radius_label->show();
+ _update_bone_list();
+ bone_paint_pos = Vector2(-100000, -100000); //send brush away when switching
+ }
+
+ uv_edit_draw->update();
+}
+
void Polygon2DEditor::_menu_option(int p_option) {
switch (p_option) {
@@ -143,6 +314,9 @@ void Polygon2DEditor::_menu_option(int p_option) {
undo_redo->commit_action();
} break;
+ case UVEDIT_GRID_SETTINGS: {
+ grid_settings->popup_centered_minsize();
+ } break;
default: {
AbstractPolygon2DEditor::_menu_option(p_option);
} break;
@@ -180,6 +354,10 @@ void Polygon2DEditor::_set_snap_step_y(float p_val) {
void Polygon2DEditor::_uv_mode(int p_mode) {
+ split_create = false;
+ uv_drag = false;
+ uv_create = false;
+
uv_mode = UVMode(p_mode);
for (int i = 0; i < UV_MODE_MAX; i++) {
uv_button[i]->set_pressed(p_mode == i);
@@ -202,8 +380,60 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_drag_from = Vector2(mb->get_position().x, mb->get_position().y);
uv_drag = true;
- uv_prev = node->get_uv();
+ points_prev = node->get_uv();
+
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ points_prev = node->get_uv();
+ } else { //edit polygon
+ points_prev = node->get_polygon();
+ }
+
uv_move_current = uv_mode;
+ if (uv_move_current == UV_MODE_CREATE) {
+
+ if (!uv_create) {
+ points_prev.resize(0);
+ Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+ points_prev.push_back(tuv);
+ uv_create_to = tuv;
+ point_drag_index = 0;
+ uv_drag_from = tuv;
+ uv_drag = true;
+ uv_create = true;
+ uv_create_uv_prev = node->get_uv();
+ uv_create_poly_prev = node->get_polygon();
+ uv_create_bones_prev = node->call("_get_bones");
+ splits_prev = node->get_splits();
+ node->set_polygon(points_prev);
+ node->set_uv(points_prev);
+
+ } else {
+ Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+
+ if (points_prev.size() > 3 && tuv.distance_to(points_prev[0]) < 8) {
+ undo_redo->create_action(TTR("Create Polygon & UV"));
+ undo_redo->add_do_method(node, "set_uv", node->get_uv());
+ undo_redo->add_undo_method(node, "set_uv", points_prev);
+ undo_redo->add_do_method(node, "set_polygon", node->get_polygon());
+ undo_redo->add_undo_method(node, "set_polygon", points_prev);
+ undo_redo->add_do_method(node, "clear_bones");
+ undo_redo->add_undo_method(node, "_set_bones", node->call("_get_bones"));
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->commit_action();
+ uv_drag = false;
+ uv_create = false;
+ _uv_mode(UV_MODE_EDIT_POINT);
+ } else {
+ points_prev.push_back(tuv);
+ point_drag_index = points_prev.size() - 1;
+ uv_drag_from = tuv;
+ }
+ node->set_polygon(points_prev);
+ node->set_uv(points_prev);
+ }
+ }
+
if (uv_move_current == UV_MODE_EDIT_POINT) {
if (mb->get_shift() && mb->get_command())
@@ -216,39 +446,219 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_EDIT_POINT) {
- uv_drag_index = -1;
- for (int i = 0; i < uv_prev.size(); i++) {
+ point_drag_index = -1;
+ for (int i = 0; i < points_prev.size(); i++) {
- Vector2 tuv = mtx.xform(uv_prev[i]);
+ Vector2 tuv = mtx.xform(points_prev[i]);
if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
uv_drag_from = tuv;
- uv_drag_index = i;
+ point_drag_index = i;
}
}
- if (uv_drag_index == -1) {
+ if (point_drag_index == -1) {
uv_drag = false;
}
}
- } else if (uv_drag) {
+
+ if (uv_move_current == UV_MODE_ADD_SPLIT) {
+
+ int split_to_index = -1;
+ split_to_index = -1;
+ for (int i = 0; i < points_prev.size(); i++) {
+
+ Vector2 tuv = mtx.xform(points_prev[i]);
+ if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
+ split_to_index = i;
+ }
+ }
+
+ if (split_to_index == -1) {
+ split_create = false;
+ return;
+ }
+
+ if (split_create) {
+
+ split_create = false;
+ if (split_to_index < point_drag_index) {
+ SWAP(split_to_index, point_drag_index);
+ }
+ bool valid = true;
+ String split_error;
+ if (split_to_index == point_drag_index) {
+ split_error = TTR("Split point with itself.");
+ valid = false;
+ }
+ if (split_to_index + 1 == point_drag_index) {
+ //not a split,goes along the edge
+ split_error = TTR("Split can't form an existing edge.");
+ valid = false;
+ }
+ if (split_to_index == points_prev.size() - 1 && point_drag_index == 0) {
+ //not a split,goes along the edge
+ split_error = TTR("Split can't form an existing edge.");
+ valid = false;
+ }
+
+ for (int i = 0; i < splits_prev.size(); i += 2) {
+
+ if (splits_prev[i] == point_drag_index && splits_prev[i + 1] == split_to_index) {
+ //already exists
+ split_error = TTR("Split already exists.");
+ valid = false;
+ break;
+ }
+
+ int a_state; //-1, outside split, 0 split point, +1, inside split
+ if (point_drag_index == splits_prev[i] || point_drag_index == splits_prev[i + 1]) {
+ a_state = 0;
+ } else if (point_drag_index < splits_prev[i] || point_drag_index > splits_prev[i + 1]) {
+ a_state = -1;
+ } else {
+ a_state = 1;
+ }
+
+ int b_state; //-1, outside split, 0 split point, +1, inside split
+ if (split_to_index == splits_prev[i] || split_to_index == splits_prev[i + 1]) {
+ b_state = 0;
+ } else if (split_to_index < splits_prev[i] || split_to_index > splits_prev[i + 1]) {
+ b_state = -1;
+ } else {
+ b_state = 1;
+ }
+
+ if (b_state * a_state < 0) {
+ //crossing
+ split_error = "Split crosses another split.";
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid) {
+
+ splits_prev.push_back(point_drag_index);
+ splits_prev.push_back(split_to_index);
+
+ undo_redo->create_action(TTR("Add Split"));
+
+ undo_redo->add_do_method(node, "set_splits", splits_prev);
+ undo_redo->add_undo_method(node, "set_splits", node->get_splits());
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->commit_action();
+ } else {
+ error->set_text(TTR("Invalid Split: ") + split_error);
+ error->popup_centered_minsize();
+ }
+
+ } else {
+ point_drag_index = split_to_index;
+ split_create = true;
+ splits_prev = node->get_splits();
+ uv_create_to = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+ }
+ }
+
+ if (uv_move_current == UV_MODE_REMOVE_SPLIT) {
+
+ for (int i = 0; i < splits_prev.size(); i += 2) {
+ if (splits_prev[i] < 0 || splits_prev[i] >= points_prev.size())
+ continue;
+ if (splits_prev[i + 1] < 0 || splits_prev[i] >= points_prev.size())
+ continue;
+ Vector2 e[2] = { mtx.xform(points_prev[splits_prev[i]]), mtx.xform(points_prev[splits_prev[i + 1]]) };
+ Vector2 mp = Vector2(mb->get_position().x, mb->get_position().y);
+ Vector2 cp = Geometry::get_closest_point_to_segment_2d(mp, e);
+ if (cp.distance_to(mp) < 8) {
+ splits_prev.remove(i);
+ splits_prev.remove(i);
+
+ undo_redo->create_action(TTR("Remove Split"));
+
+ undo_redo->add_do_method(node, "set_splits", splits_prev);
+ undo_redo->add_undo_method(node, "set_splits", node->get_splits());
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->commit_action();
+
+ break;
+ }
+ }
+ }
+
+ if (uv_move_current == UV_MODE_PAINT_WEIGHT || uv_move_current == UV_MODE_CLEAR_WEIGHT) {
+
+ int bone_selected = -1;
+ for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {
+ CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));
+ if (c && c->is_pressed()) {
+ bone_selected = i;
+ break;
+ }
+ }
+
+ if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == points_prev.size()) {
+
+ prev_weights = node->get_bone_weights(bone_selected);
+ bone_painting = true;
+ bone_painting_bone = bone_selected;
+ }
+ }
+
+ } else if (uv_drag && !uv_create) {
undo_redo->create_action(TTR("Transform UV Map"));
- undo_redo->add_do_method(node, "set_uv", node->get_uv());
- undo_redo->add_undo_method(node, "set_uv", uv_prev);
+
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ undo_redo->add_do_method(node, "set_uv", node->get_uv());
+ undo_redo->add_undo_method(node, "set_uv", points_prev);
+ } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
+ undo_redo->add_do_method(node, "set_polygon", node->get_polygon());
+ undo_redo->add_undo_method(node, "set_polygon", points_prev);
+ }
undo_redo->add_do_method(uv_edit_draw, "update");
undo_redo->add_undo_method(uv_edit_draw, "update");
undo_redo->commit_action();
uv_drag = false;
+ } else if (bone_painting) {
+
+ undo_redo->create_action(TTR("Paint bone weights"));
+ undo_redo->add_do_method(node, "set_bone_weights", bone_painting_bone, node->get_bone_weights(bone_painting_bone));
+ undo_redo->add_undo_method(node, "set_bone_weights", bone_painting_bone, prev_weights);
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->commit_action();
+ bone_painting = false;
}
} else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
- if (uv_drag) {
+ if (uv_create) {
+
+ uv_drag = false;
+ uv_create = false;
+ node->set_uv(uv_create_uv_prev);
+ node->set_polygon(uv_create_poly_prev);
+ node->call("_set_bones", uv_create_bones_prev);
+ node->set_splits(splits_prev);
+ uv_edit_draw->update();
+ } else if (uv_drag) {
uv_drag = false;
- node->set_uv(uv_prev);
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ node->set_uv(points_prev);
+ } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
+ node->set_polygon(points_prev);
+ }
uv_edit_draw->update();
+ } else if (split_create) {
+ split_create = false;
+ uv_edit_draw->update();
+ } else if (bone_painting) {
+ node->set_bone_weights(bone_painting_bone, prev_weights);
}
} else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
@@ -277,48 +687,66 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
switch (uv_move_current) {
+ case UV_MODE_CREATE: {
+ if (uv_create) {
+ uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y));
+ }
+ } break;
case UV_MODE_EDIT_POINT: {
- PoolVector<Vector2> uv_new = uv_prev;
- uv_new.set(uv_drag_index, uv_new[uv_drag_index] + drag);
- node->set_uv(uv_new);
+ PoolVector<Vector2> uv_new = points_prev;
+ uv_new.set(point_drag_index, uv_new[point_drag_index] + drag);
+
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ node->set_uv(uv_new);
+ } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
+ node->set_polygon(uv_new);
+ }
} break;
case UV_MODE_MOVE: {
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
uv_new.set(i, uv_new[i] + drag);
- node->set_uv(uv_new);
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ node->set_uv(uv_new);
+ } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
+ node->set_polygon(uv_new);
+ }
} break;
case UV_MODE_ROTATE: {
Vector2 center;
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
- center += uv_prev[i];
+ center += points_prev[i];
center /= uv_new.size();
float angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized());
for (int i = 0; i < uv_new.size(); i++) {
- Vector2 rel = uv_prev[i] - center;
+ Vector2 rel = points_prev[i] - center;
rel = rel.rotated(angle);
uv_new.set(i, center + rel);
}
- node->set_uv(uv_new);
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ node->set_uv(uv_new);
+ } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
+ node->set_polygon(uv_new);
+ }
} break;
case UV_MODE_SCALE: {
Vector2 center;
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
- center += uv_prev[i];
+ center += points_prev[i];
center /= uv_new.size();
float from_dist = uv_drag_from.distance_to(mtx.xform(center));
@@ -329,14 +757,51 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
float scale = to_dist / from_dist;
for (int i = 0; i < uv_new.size(); i++) {
- Vector2 rel = uv_prev[i] - center;
+ Vector2 rel = points_prev[i] - center;
rel = rel * scale;
uv_new.set(i, center + rel);
}
- node->set_uv(uv_new);
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ node->set_uv(uv_new);
+ } else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
+ node->set_polygon(uv_new);
+ }
} break;
}
+
+ if (bone_painting) {
+ bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ PoolVector<float> painted_weights = node->get_bone_weights(bone_painting_bone);
+
+ {
+ int pc = painted_weights.size();
+ float amount = bone_paint_strength->get_value();
+ float radius = bone_paint_radius->get_value() * EDSCALE;
+
+ if (uv_mode == UV_MODE_CLEAR_WEIGHT) {
+ amount = -amount;
+ }
+
+ PoolVector<float>::Write w = painted_weights.write();
+ PoolVector<float>::Read r = prev_weights.read();
+ PoolVector<Vector2>::Read rv = points_prev.read();
+
+ for (int i = 0; i < pc; i++) {
+ if (mtx.xform(rv[i]).distance_to(bone_paint_pos) < radius) {
+ w[i] = CLAMP(r[i] + amount, 0, 1);
+ }
+ }
+ }
+
+ node->set_bone_weights(bone_painting_bone, painted_weights);
+ }
+ uv_edit_draw->update();
+ } else if (split_create) {
+ uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y));
+ uv_edit_draw->update();
+ } else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) {
+ bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
uv_edit_draw->update();
}
}
@@ -372,6 +837,8 @@ void Polygon2DEditor::_uv_draw() {
if (base_tex.is_null())
return;
+ String warning;
+
Transform2D mtx;
mtx.elements[2] = -uv_draw_ofs;
mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom));
@@ -407,7 +874,31 @@ void Polygon2DEditor::_uv_draw() {
}
}
- PoolVector<Vector2> uvs = node->get_uv();
+ PoolVector<Vector2> uvs;
+ if (uv_edit_mode[0]->is_pressed()) { //edit uv
+ uvs = node->get_uv();
+ } else { //edit polygon
+ uvs = node->get_polygon();
+ }
+
+ PoolVector<float>::Read weight_r;
+
+ if (uv_edit_mode[3]->is_pressed()) {
+ int bone_selected = -1;
+ for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {
+ CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));
+ if (c && c->is_pressed()) {
+ bone_selected = i;
+ break;
+ }
+ }
+
+ if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uvs.size()) {
+
+ weight_r = node->get_bone_weights(bone_selected).read();
+ }
+ }
+
Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
Rect2 rect(Point2(), mtx.basis_xform(base_tex->get_size()));
@@ -416,11 +907,87 @@ void Polygon2DEditor::_uv_draw() {
for (int i = 0; i < uvs.size(); i++) {
int next = (i + 1) % uvs.size();
- uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(uvs[next]), Color(0.9, 0.5, 0.5), 2);
- uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5);
+ Vector2 next_point = uvs[next];
+ if (uv_create && i == uvs.size() - 1) {
+ next_point = uv_create_to;
+ }
+ uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), Color(0.9, 0.5, 0.5), 2);
+ if (weight_r.ptr()) {
+
+ Vector2 draw_pos = mtx.xform(uvs[i]);
+ float weight = weight_r[i];
+ uv_edit_draw->draw_rect(Rect2(draw_pos - Vector2(2, 2) * EDSCALE, Vector2(5, 5) * EDSCALE), Color(weight, weight, weight, 1.0));
+ } else {
+ uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5);
+ }
rect.expand_to(mtx.basis_xform(uvs[i]));
}
+ if (split_create) {
+ Vector2 from = uvs[point_drag_index];
+ Vector2 to = uv_create_to;
+ uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), Color(0.9, 0.5, 0.5), 2);
+ }
+
+ PoolVector<int> splits = node->get_splits();
+
+ for (int i = 0; i < splits.size(); i += 2) {
+ int idx_from = splits[i];
+ int idx_to = splits[i + 1];
+ if (idx_from < 0 || idx_to >= uvs.size())
+ continue;
+ uv_edit_draw->draw_line(mtx.xform(uvs[idx_from]), mtx.xform(uvs[idx_to]), Color(0.9, 0.5, 0.5), 2);
+ }
+
+ if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) {
+
+ NodePath bone_path;
+ for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {
+ CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));
+ if (c && c->is_pressed()) {
+ bone_path = node->get_bone_path(i);
+ break;
+ }
+ }
+
+ //draw skeleton
+ NodePath skeleton_path = node->get_skeleton();
+ if (node->has_node(skeleton_path)) {
+ Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node(skeleton_path));
+ if (skeleton) {
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+
+ Bone2D *bone = skeleton->get_bone(i);
+ if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0))
+ continue; //not set
+
+ bool current = bone_path == skeleton->get_path_to(bone);
+
+ for (int j = 0; j < bone->get_child_count(); j++) {
+
+ Node2D *n = Object::cast_to<Node2D>(bone->get_child(j));
+ if (!n)
+ continue;
+
+ bool edit_bone = n->has_meta("_edit_bone_") && n->get_meta("_edit_bone_");
+ if (edit_bone) {
+
+ Transform2D bone_xform = node->get_global_transform().affine_inverse() * (skeleton->get_global_transform() * bone->get_skeleton_rest());
+ Transform2D endpoint_xform = bone_xform * n->get_transform();
+
+ Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), current ? 5 : 4);
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, current ? 3 : 2);
+ }
+ }
+ }
+ }
+ }
+
+ //draw paint circle
+ uv_edit_draw->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1));
+ }
+
rect = rect.grow(200);
updating_uv_scroll = true;
uv_hscroll->set_min(rect.position.x);
@@ -449,6 +1016,11 @@ void Polygon2DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_snap_off_y"), &Polygon2DEditor::_set_snap_off_y);
ClassDB::bind_method(D_METHOD("_set_snap_step_x"), &Polygon2DEditor::_set_snap_step_x);
ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &Polygon2DEditor::_set_snap_step_y);
+ ClassDB::bind_method(D_METHOD("_uv_edit_mode_select"), &Polygon2DEditor::_uv_edit_mode_select);
+ ClassDB::bind_method(D_METHOD("_sync_bones"), &Polygon2DEditor::_sync_bones);
+ ClassDB::bind_method(D_METHOD("_update_bone_list"), &Polygon2DEditor::_update_bone_list);
+
+ ClassDB::bind_method(D_METHOD("_bone_paint_selected"), &Polygon2DEditor::_bone_paint_selected);
}
Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
@@ -481,6 +1053,40 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_edit->add_child(uv_main_vb);
//uv_edit->set_child_rect(uv_main_vb);
HBoxContainer *uv_mode_hb = memnew(HBoxContainer);
+
+ uv_edit_group.instance();
+
+ uv_edit_mode[0] = memnew(ToolButton);
+ uv_mode_hb->add_child(uv_edit_mode[0]);
+ uv_edit_mode[0]->set_toggle_mode(true);
+ uv_edit_mode[1] = memnew(ToolButton);
+ uv_mode_hb->add_child(uv_edit_mode[1]);
+ uv_edit_mode[1]->set_toggle_mode(true);
+ uv_edit_mode[2] = memnew(ToolButton);
+ uv_mode_hb->add_child(uv_edit_mode[2]);
+ uv_edit_mode[2]->set_toggle_mode(true);
+ uv_edit_mode[3] = memnew(ToolButton);
+ uv_mode_hb->add_child(uv_edit_mode[3]);
+ uv_edit_mode[3]->set_toggle_mode(true);
+
+ uv_edit_mode[0]->set_text(TTR("UV"));
+ uv_edit_mode[0]->set_pressed(true);
+ uv_edit_mode[1]->set_text(TTR("Poly"));
+ uv_edit_mode[2]->set_text(TTR("Splits"));
+ uv_edit_mode[3]->set_text(TTR("Bones"));
+
+ uv_edit_mode[0]->set_button_group(uv_edit_group);
+ uv_edit_mode[1]->set_button_group(uv_edit_group);
+ uv_edit_mode[2]->set_button_group(uv_edit_group);
+ uv_edit_mode[3]->set_button_group(uv_edit_group);
+
+ uv_edit_mode[0]->connect("pressed", this, "_uv_edit_mode_select", varray(0));
+ uv_edit_mode[1]->connect("pressed", this, "_uv_edit_mode_select", varray(1));
+ uv_edit_mode[2]->connect("pressed", this, "_uv_edit_mode_select", varray(2));
+ uv_edit_mode[3]->connect("pressed", this, "_uv_edit_mode_select", varray(3));
+
+ uv_mode_hb->add_child(memnew(VSeparator));
+
uv_main_vb->add_child(uv_mode_hb);
for (int i = 0; i < UV_MODE_MAX; i++) {
@@ -491,12 +1097,45 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_button[i]->set_focus_mode(FOCUS_NONE);
}
- uv_button[0]->set_tooltip(TTR("Move Point") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale"));
- uv_button[1]->set_tooltip(TTR("Move Polygon"));
- uv_button[2]->set_tooltip(TTR("Rotate Polygon"));
- uv_button[3]->set_tooltip(TTR("Scale Polygon"));
+ uv_button[0]->set_tooltip(TTR("Create Polygon"));
+ uv_button[1]->set_tooltip(TTR("Move Point") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale"));
+ uv_button[2]->set_tooltip(TTR("Move Polygon"));
+ uv_button[3]->set_tooltip(TTR("Rotate Polygon"));
+ uv_button[4]->set_tooltip(TTR("Scale Polygon"));
+ uv_button[5]->set_tooltip(TTR("Connect two points to make a split"));
+ uv_button[6]->set_tooltip(TTR("Select a split to erase it"));
+ uv_button[7]->set_tooltip(TTR("Paint weights with specified intensity"));
+ uv_button[8]->set_tooltip(TTR("UnPaint weights with specified intensity"));
+
+ uv_button[0]->hide();
+ uv_button[5]->hide();
+ uv_button[6]->hide();
+ uv_button[7]->hide();
+ uv_button[8]->hide();
+ uv_button[1]->set_pressed(true);
+
+ bone_paint_strength = memnew(HSlider);
+ uv_mode_hb->add_child(bone_paint_strength);
+ bone_paint_strength->set_custom_minimum_size(Size2(75 * EDSCALE, 0));
+ bone_paint_strength->set_v_size_flags(SIZE_SHRINK_CENTER);
+ bone_paint_strength->set_min(0);
+ bone_paint_strength->set_max(1);
+ bone_paint_strength->set_step(0.01);
+ bone_paint_strength->set_value(0.5);
+
+ bone_paint_radius_label = memnew(Label(" " + TTR("Radius:") + " "));
+ uv_mode_hb->add_child(bone_paint_radius_label);
+ bone_paint_radius = memnew(SpinBox);
+ uv_mode_hb->add_child(bone_paint_radius);
+
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+ bone_paint_radius->set_min(1);
+ bone_paint_radius->set_max(100);
+ bone_paint_radius->set_step(1);
+ bone_paint_radius->set_value(32);
- uv_button[0]->set_pressed(true);
HBoxContainer *uv_main_hb = memnew(HBoxContainer);
uv_main_vb->add_child(uv_main_hb);
uv_edit_draw = memnew(Control);
@@ -510,6 +1149,8 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_menu->get_popup()->add_item(TTR("UV->Polygon"), UVEDIT_UV_TO_POLYGON);
uv_menu->get_popup()->add_separator();
uv_menu->get_popup()->add_item(TTR("Clear UV"), UVEDIT_UV_CLEAR);
+ uv_menu->get_popup()->add_separator();
+ uv_menu->get_popup()->add_item(TTR("Grid Settings"), UVEDIT_GRID_SETTINGS);
uv_menu->get_popup()->connect("id_pressed", this, "_menu_option");
uv_mode_hb->add_child(memnew(VSeparator));
@@ -532,8 +1173,11 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
b_snap_grid->set_tooltip(TTR("Show Grid"));
b_snap_grid->connect("toggled", this, "_set_show_grid");
- uv_mode_hb->add_child(memnew(VSeparator));
- uv_mode_hb->add_child(memnew(Label(TTR("Grid Offset:"))));
+ grid_settings = memnew(AcceptDialog);
+ grid_settings->set_title(TTR("Configure Grid:"));
+ add_child(grid_settings);
+ VBoxContainer *grid_settings_vb = memnew(VBoxContainer);
+ grid_settings->add_child(grid_settings_vb);
SpinBox *sb_off_x = memnew(SpinBox);
sb_off_x->set_min(-256);
@@ -542,7 +1186,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_off_x->set_value(snap_offset.x);
sb_off_x->set_suffix("px");
sb_off_x->connect("value_changed", this, "_set_snap_off_x");
- uv_mode_hb->add_child(sb_off_x);
+ grid_settings_vb->add_margin_child(TTR("Grid Offset X:"), sb_off_x);
SpinBox *sb_off_y = memnew(SpinBox);
sb_off_y->set_min(-256);
@@ -551,10 +1195,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_off_y->set_value(snap_offset.y);
sb_off_y->set_suffix("px");
sb_off_y->connect("value_changed", this, "_set_snap_off_y");
- uv_mode_hb->add_child(sb_off_y);
-
- uv_mode_hb->add_child(memnew(VSeparator));
- uv_mode_hb->add_child(memnew(Label(TTR("Grid Step:"))));
+ grid_settings_vb->add_margin_child(TTR("Grid Offset Y:"), sb_off_y);
SpinBox *sb_step_x = memnew(SpinBox);
sb_step_x->set_min(-256);
@@ -563,7 +1204,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_step_x->set_value(snap_step.x);
sb_step_x->set_suffix("px");
sb_step_x->connect("value_changed", this, "_set_snap_step_x");
- uv_mode_hb->add_child(sb_step_x);
+ grid_settings_vb->add_margin_child(TTR("Grid Step X:"), sb_step_x);
SpinBox *sb_step_y = memnew(SpinBox);
sb_step_y->set_min(-256);
@@ -572,7 +1213,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_step_y->set_value(snap_step.y);
sb_step_y->set_suffix("px");
sb_step_y->connect("value_changed", this, "_set_snap_step_y");
- uv_mode_hb->add_child(sb_step_y);
+ grid_settings_vb->add_margin_child(TTR("Grid Step Y:"), sb_step_y);
uv_mode_hb->add_child(memnew(VSeparator));
uv_icon_zoom = memnew(TextureRect);
@@ -582,8 +1223,10 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_zoom->set_max(4);
uv_zoom->set_value(1);
uv_zoom->set_step(0.01);
+ uv_zoom->set_v_size_flags(SIZE_SHRINK_CENTER);
+
uv_mode_hb->add_child(uv_zoom);
- uv_zoom->set_custom_minimum_size(Size2(200, 0));
+ uv_zoom->set_custom_minimum_size(Size2(80 * EDSCALE, 0));
uv_zoom_value = memnew(SpinBox);
uv_zoom->share(uv_zoom_value);
uv_zoom_value->set_custom_minimum_size(Size2(50, 0));
@@ -597,12 +1240,29 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_main_vb->add_child(uv_hscroll);
uv_hscroll->connect("value_changed", this, "_uv_scroll_changed");
+ bone_scroll_main_vb = memnew(VBoxContainer);
+ bone_scroll_main_vb->hide();
+ sync_bones = memnew(Button(TTR("Sync Bones to Polygon")));
+ bone_scroll_main_vb->add_child(sync_bones);
+ uv_main_hb->add_child(bone_scroll_main_vb);
+ bone_scroll = memnew(ScrollContainer);
+ bone_scroll->set_v_scroll(true);
+ bone_scroll->set_h_scroll(false);
+ bone_scroll_main_vb->add_child(bone_scroll);
+ bone_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+ bone_scroll_vb = memnew(VBoxContainer);
+ bone_scroll->add_child(bone_scroll_vb);
+ sync_bones->connect("pressed", this, "_sync_bones");
+
uv_edit_draw->connect("draw", this, "_uv_draw");
uv_edit_draw->connect("gui_input", this, "_uv_input");
uv_draw_zoom = 1.0;
- uv_drag_index = -1;
+ point_drag_index = -1;
uv_drag = false;
+ uv_create = false;
updating_uv_scroll = false;
+ split_create = false;
+ bone_painting = false;
error = memnew(AcceptDialog);
add_child(error);
diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h
index f1472e4522..f9b42a21c2 100644
--- a/editor/plugins/polygon_2d_editor_plugin.h
+++ b/editor/plugins/polygon_2d_editor_plugin.h
@@ -32,7 +32,7 @@
#define POLYGON_2D_EDITOR_PLUGIN_H
#include "editor/plugins/abstract_polygon_2d_editor.h"
-
+#include "scene/gui/scroll_container.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -45,23 +45,32 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
MODE_EDIT_UV = MODE_CONT,
UVEDIT_POLYGON_TO_UV,
UVEDIT_UV_TO_POLYGON,
- UVEDIT_UV_CLEAR
+ UVEDIT_UV_CLEAR,
+ UVEDIT_GRID_SETTINGS
};
enum UVMode {
+ UV_MODE_CREATE,
UV_MODE_EDIT_POINT,
UV_MODE_MOVE,
UV_MODE_ROTATE,
UV_MODE_SCALE,
+ UV_MODE_ADD_SPLIT,
+ UV_MODE_REMOVE_SPLIT,
+ UV_MODE_PAINT_WEIGHT,
+ UV_MODE_CLEAR_WEIGHT,
UV_MODE_MAX
};
+ ToolButton *uv_edit_mode[4];
+ Ref<ButtonGroup> uv_edit_group;
+
Polygon2D *node;
UVMode uv_mode;
AcceptDialog *uv_edit;
- ToolButton *uv_button[4];
+ ToolButton *uv_button[UV_MODE_MAX];
ToolButton *b_snap_enable;
ToolButton *b_snap_grid;
Control *uv_edit_draw;
@@ -72,11 +81,35 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
MenuButton *uv_menu;
TextureRect *uv_icon_zoom;
+ VBoxContainer *bone_scroll_main_vb;
+ ScrollContainer *bone_scroll;
+ VBoxContainer *bone_scroll_vb;
+ Button *sync_bones;
+ HSlider *bone_paint_strength;
+ SpinBox *bone_paint_radius;
+ Label *bone_paint_radius_label;
+ bool bone_painting;
+ int bone_painting_bone;
+ PoolVector<float> prev_weights;
+ Vector2 bone_paint_pos;
+ AcceptDialog *grid_settings;
+
+ void _sync_bones();
+ void _update_bone_list();
+
Vector2 uv_draw_ofs;
float uv_draw_zoom;
- PoolVector<Vector2> uv_prev;
- int uv_drag_index;
+ PoolVector<Vector2> points_prev;
+ PoolVector<Vector2> uv_create_uv_prev;
+ PoolVector<Vector2> uv_create_poly_prev;
+ Array uv_create_bones_prev;
+ PoolVector<int> splits_prev;
+
+ Vector2 uv_create_to;
+ int point_drag_index;
bool uv_drag;
+ bool uv_create;
+ bool split_create;
UVMode uv_move_current;
Vector2 uv_drag_from;
bool updating_uv_scroll;
@@ -104,6 +137,9 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
void _set_snap_step_x(float p_val);
void _set_snap_step_y(float p_val);
+ void _uv_edit_mode_select(int p_mode);
+ void _bone_paint_selected(int p_index);
+
protected:
virtual Node2D *_get_node() const;
virtual void _set_node(Node *p_polygon);
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index a47cb40240..c6e8ec1a2b 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -229,17 +229,34 @@ void ResourcePreloaderEditor::_update_library() {
ERR_CONTINUE(r.is_null());
ti->set_tooltip(0, r->get_path());
- String type = r->get_class();
- ti->set_text(1, type);
+ ti->set_text(1, r->get_path());
+ ti->add_button(1, get_icon("InstanceOptions", "EditorIcons"), BUTTON_SUBSCENE, false, TTR("Open in Editor"));
+ ti->set_tooltip(1, TTR("Instance:") + " " + r->get_path() + "\n" + TTR("Type:") + " " + r->get_class());
+ ti->set_editable(1, false);
ti->set_selectable(1, false);
+ String type = r->get_class();
+ ti->set_text(2, type);
+ ti->set_selectable(2, false);
if (has_icon(type, "EditorIcons"))
- ti->set_icon(1, get_icon(type, "EditorIcons"));
+ ti->set_icon(2, get_icon(type, "EditorIcons"));
}
//player->add_resource("default",resource);
}
+void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) {
+
+ TreeItem *item = Object::cast_to<TreeItem>(p_item);
+ ERR_FAIL_COND(!item);
+
+ String rpath = item->get_text(p_column);
+
+ if (p_id == BUTTON_SUBSCENE) {
+ EditorInterface::get_singleton()->open_scene_from_path(rpath);
+ }
+}
+
void ResourcePreloaderEditor::edit(ResourcePreloader *p_preloader) {
preloader = p_preloader;
@@ -354,6 +371,7 @@ void ResourcePreloaderEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_delete_confirm_pressed"), &ResourcePreloaderEditor::_delete_confirm_pressed);
ClassDB::bind_method(D_METHOD("_files_load_request"), &ResourcePreloaderEditor::_files_load_request);
ClassDB::bind_method(D_METHOD("_update_library"), &ResourcePreloaderEditor::_update_library);
+ ClassDB::bind_method(D_METHOD("_cell_button_pressed"), &ResourcePreloaderEditor::_cell_button_pressed);
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &ResourcePreloaderEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &ResourcePreloaderEditor::can_drop_data_fw);
@@ -385,11 +403,14 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() {
add_child(file);
tree = memnew(Tree);
- tree->set_columns(2);
+ tree->connect("button_pressed", this, "_cell_button_pressed");
+ tree->set_columns(3);
tree->set_column_min_width(0, 3);
tree->set_column_min_width(1, 1);
+ tree->set_column_min_width(2, 1);
tree->set_column_expand(0, true);
tree->set_column_expand(1, true);
+ tree->set_column_expand(2, true);
tree->set_v_size_flags(SIZE_EXPAND_FILL);
tree->set_drag_forwarding(this);
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index 6e04c70741..e737157785 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -42,6 +42,10 @@ class ResourcePreloaderEditor : public PanelContainer {
GDCLASS(ResourcePreloaderEditor, PanelContainer);
+ enum {
+ BUTTON_SUBSCENE = 0,
+ };
+
Button *load;
Button *_delete;
Button *paste;
@@ -61,6 +65,7 @@ class ResourcePreloaderEditor : public PanelContainer {
void _delete_pressed();
void _delete_confirm_pressed();
void _update_library();
+ void _cell_button_pressed(Object *p_item, int p_column, int p_id);
void _item_edited();
UndoRedo *undo_redo;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index d18422c0c0..fa674e1e34 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -39,9 +39,11 @@
#include "core/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/script_editor_debugger.h"
#include "scene/main/viewport.h"
+#include "script_text_editor.h"
/*** SCRIPT EDITOR ****/
@@ -54,6 +56,8 @@ void ScriptEditorBase::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("request_save_history"));
ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
+ // TODO This signal is no use for VisualScript...
+ ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
}
static bool _can_open_in_editor(Script *p_script) {
@@ -148,8 +152,6 @@ public:
}
};
-#define SORT_SCRIPT_LIST
-
void ScriptEditorQuickOpen::popup(const Vector<String> &p_functions, bool p_dontclear) {
popup_centered_ratio(0.6);
@@ -300,37 +302,34 @@ void ScriptEditor::_script_created(Ref<Script> p_script) {
void ScriptEditor::_goto_script_line2(int p_line) {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
- return;
-
- current->goto_line(p_line);
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->goto_line(p_line);
}
void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
Ref<Script> script = Object::cast_to<Script>(*p_script);
- if (!script.is_null() && script->get_path().is_resource_file()) {
+ if (!script.is_null() && script->has_source_code()) {
if (edit(p_script, p_line, 0)) {
editor->push_item(p_script.ptr());
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
- return;
-
- current->goto_line(p_line, true);
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->goto_line(p_line, true);
}
}
}
+ScriptEditorBase *ScriptEditor::_get_current_editor() const {
+
+ int selected = tab_container->get_current_tab();
+ if (selected < 0 || selected >= tab_container->get_child_count())
+ return NULL;
+
+ return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+}
+
void ScriptEditor::_update_history_arrows() {
script_back->set_disabled(history_pos <= 0);
@@ -429,36 +428,32 @@ void ScriptEditor::_add_recent_script(String p_path) {
return;
}
- // remove if already stored
- int already_recent = previous_scripts.find(p_path);
- if (already_recent >= 0) {
- previous_scripts.remove(already_recent);
+ Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
+ if (rc.find(p_path) != -1) {
+ rc.erase(p_path);
+ }
+ rc.push_front(p_path);
+ if (rc.size() > 10) {
+ rc.resize(10);
}
- // add to list
- previous_scripts.insert(0, p_path);
-
+ EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
_update_recent_scripts();
}
void ScriptEditor::_update_recent_scripts() {
- // make sure we don't exceed max size
- const int max_history = EDITOR_DEF("text_editor/files/maximum_recent_files", 20);
- if (previous_scripts.size() > max_history) {
- previous_scripts.resize(max_history);
- }
-
+ Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
recent_scripts->clear();
recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/open_recent", TTR("Open Recent"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T));
recent_scripts->add_separator();
- const int max_shown = 8;
- for (int i = 0; i < previous_scripts.size() && i <= max_shown; i++) {
- String path = previous_scripts.get(i);
- // just show script name and last dir
- recent_scripts->add_item(path.get_slice("/", path.get_slice_count("/") - 2) + "/" + path.get_file());
+ String path;
+ for (int i = 0; i < rc.size(); i++) {
+
+ path = rc[i];
+ recent_scripts->add_item(path.replace("res://", ""));
}
recent_scripts->add_separator();
@@ -471,7 +466,7 @@ void ScriptEditor::_open_recent_script(int p_idx) {
// clear button
if (p_idx == recent_scripts->get_item_count() - 1) {
- previous_scripts.clear();
+ EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", Array());
call_deferred("_update_recent_scripts");
return;
}
@@ -481,25 +476,37 @@ void ScriptEditor::_open_recent_script(int p_idx) {
p_idx -= 2;
}
- if (p_idx < previous_scripts.size() && p_idx >= 0) {
+ Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
+ ERR_FAIL_INDEX(p_idx, rc.size());
- String path = previous_scripts.get(p_idx);
- // if its not on disk its a help file or deleted
- if (FileAccess::exists(path)) {
- Ref<Script> script = ResourceLoader::load(path);
- if (script.is_valid()) {
- edit(script, true);
- }
- // if it's a path then its most likely a delted file not help
- } else if (!path.is_resource_file()) {
- _help_class_open(path);
+ String path = rc[p_idx];
+ // if its not on disk its a help file or deleted
+ if (FileAccess::exists(path)) {
+ Ref<Script> script = ResourceLoader::load(path);
+ if (script.is_valid()) {
+ edit(script, true);
+ return;
}
- previous_scripts.remove(p_idx);
- _update_recent_scripts();
+
+ // if it's a path then its most likely a deleted file not help
+ } else if (!path.is_resource_file()) {
+ _help_class_open(path);
+ return;
}
+
+ rc.remove(p_idx);
+ EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
+ _update_recent_scripts();
+ _show_error_dialog(path);
+}
+
+void ScriptEditor::_show_error_dialog(String p_path) {
+
+ error_dialog->set_text(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_path));
+ error_dialog->popup_centered_minsize();
}
-void ScriptEditor::_close_tab(int p_idx, bool p_save) {
+void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
int selected = p_idx;
if (selected < 0 || selected >= tab_container->get_child_count())
@@ -508,18 +515,16 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save) {
Node *tselected = tab_container->get_child(selected);
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
if (current) {
- _add_recent_script(current->get_edited_script()->get_path());
if (p_save) {
apply_scripts();
}
notify_script_close(current->get_edited_script());
- } else {
- EditorHelp *help = Object::cast_to<EditorHelp>(tab_container->get_child(selected));
- _add_recent_script(help->get_class());
}
// roll back to previous tab
- _history_back();
+ if (p_history_back) {
+ _history_back();
+ }
//remove from history
history.resize(history_pos + 1);
@@ -577,13 +582,13 @@ void ScriptEditor::_close_docs_tab() {
EditorHelp *se = Object::cast_to<EditorHelp>(tab_container->get_child(i));
if (se) {
- _close_tab(i);
+ _close_tab(i, true, false);
}
}
}
void ScriptEditor::_copy_script_path() {
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ ScriptEditorBase *se = _get_current_editor();
Ref<Script> script = se->get_edited_script();
OS::get_singleton()->set_clipboard(script->get_path());
}
@@ -815,11 +820,8 @@ void ScriptEditor::_file_dialog_action(String p_file) {
Ref<Script> ScriptEditor::_get_current_script() {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return NULL;
+ ScriptEditorBase *current = _get_current_editor();
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
if (current) {
return current->get_edited_script();
} else {
@@ -882,7 +884,7 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog->add_filter("*.tet");
file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
file_dialog->popup_centered_ratio();
- file_dialog->set_title(TTR("Save Theme As.."));
+ file_dialog->set_title(TTR("Save Theme As..."));
} break;
case SEARCH_HELP: {
@@ -935,11 +937,7 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+ ScriptEditorBase *current = _get_current_editor();
if (current) {
switch (p_option) {
@@ -1029,7 +1027,7 @@ void ScriptEditor::_menu_option(int p_option) {
_copy_script_path();
} break;
case SHOW_IN_FILE_SYSTEM: {
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ ScriptEditorBase *se = _get_current_editor();
Ref<Script> script = se->get_edited_script();
FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
file_system_dock->navigate_to_path(script->get_path());
@@ -1219,6 +1217,17 @@ void ScriptEditor::_notification(int p_what) {
recent_scripts->set_as_minsize();
} break;
+ case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
+
+ if (is_visible()) {
+ find_in_files_button->show();
+ } else {
+ find_in_files->hide();
+ find_in_files_button->hide();
+ }
+
+ } break;
+
default:
break;
}
@@ -1226,15 +1235,11 @@ void ScriptEditor::_notification(int p_what) {
bool ScriptEditor::can_take_away_focus() const {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return true;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ return current->can_lose_focus_on_node_selection();
+ else
return true;
-
- return current->can_lose_focus_on_node_selection();
}
void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
@@ -1311,28 +1316,22 @@ void ScriptEditor::ensure_focus_current() {
if (!is_inside_tree())
return;
- int cidx = tab_container->get_current_tab();
- if (cidx < 0 || cidx >= tab_container->get_tab_count())
- return;
-
- Control *c = Object::cast_to<Control>(tab_container->get_child(cidx));
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(c);
- if (!se)
- return;
- se->ensure_focus();
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->ensure_focus();
}
void ScriptEditor::_members_overview_selected(int p_idx) {
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
return;
}
- // Go to the member's line and reset the cursor column. We can't just change scroll_position
- // directly, since code might be folded.
+ // Go to the member's line and reset the cursor column. We can't change scroll_position
+ // directly until we have gone to the line first, since code might be folded.
se->goto_line(members_overview->get_item_metadata(p_idx));
Dictionary state = se->get_edit_state();
state["column"] = 0;
+ state["scroll_position"] = members_overview->get_item_metadata(p_idx);
se->set_edit_state(state);
}
@@ -1357,18 +1356,12 @@ void ScriptEditor::ensure_select_current() {
if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) {
- Node *current = tab_container->get_child(tab_container->get_current_tab());
-
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (se) {
- Ref<Script> script = se->get_edited_script();
-
if (!grab_focus_block && is_visible_in_tree())
se->ensure_focus();
}
-
- EditorHelp *eh = Object::cast_to<EditorHelp>(current);
}
_update_selected_editor_menu();
@@ -1408,12 +1401,7 @@ struct _ScriptEditorItemData {
void ScriptEditor::_update_members_overview_visibility() {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
members_overview->set_visible(false);
return;
@@ -1429,12 +1417,7 @@ void ScriptEditor::_update_members_overview_visibility() {
void ScriptEditor::_update_members_overview() {
members_overview->clear();
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
return;
}
@@ -1573,6 +1556,9 @@ void ScriptEditor::_update_script_names() {
case SORT_BY_PATH: {
sd.sort_key = path;
} break;
+ case SORT_BY_NONE: {
+ sd.sort_key = "";
+ } break;
}
switch (display_as) {
@@ -1690,28 +1676,42 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path");
String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
- Dictionary keys;
- keys["project"] = ProjectSettings::get_singleton()->get_resource_path();
- keys["file"] = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
- keys["line"] = p_line >= 0 ? p_line : 0;
- keys["col"] = p_col;
-
- flags = flags.format(keys).strip_edges().replace("\\\\", "\\");
-
List<String> args;
if (flags.size()) {
- int from = 0, to = 0;
+ String project_path = ProjectSettings::get_singleton()->get_resource_path();
+ String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
+
+ flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0));
+ flags = flags.replacen("{col}", itos(p_col));
+ flags = flags.strip_edges().replace("\\\\", "\\");
+
+ int from = 0;
+ int num_chars = 0;
bool inside_quotes = false;
+
for (int i = 0; i < flags.size(); i++) {
+
if (flags[i] == '"' && (!i || flags[i - 1] != '\\')) {
+
+ if (!inside_quotes) {
+ from++;
+ }
inside_quotes = !inside_quotes;
+
} else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) {
- args.push_back(flags.substr(from, to));
+
+ String arg = flags.substr(from, num_chars);
+
+ // do path replacement here, else there will be issues with spaces and quotes
+ arg = arg.replacen("{project}", project_path);
+ arg = arg.replacen("{file}", script_path);
+ args.push_back(arg);
+
from = i + 1;
- to = 0;
+ num_chars = 0;
} else {
- to++;
+ num_chars++;
}
}
}
@@ -1756,6 +1756,20 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
}
ERR_FAIL_COND_V(!se, false);
+ bool highlighter_set = false;
+ for (int i = 0; i < syntax_highlighters_func_count; i++) {
+ SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
+ se->add_syntax_highlighter(highlighter);
+
+ if (!highlighter_set) {
+ List<String> languages = highlighter->get_supported_languages();
+ if (languages.find(p_script->get_language()->get_name())) {
+ se->set_syntax_highlighter(highlighter);
+ highlighter_set = true;
+ }
+ }
+ }
+
tab_container->add_child(se);
se->set_edited_script(p_script);
se->set_tooltip_request_func("_get_debug_tooltip", this);
@@ -1777,6 +1791,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
se->connect("request_open_script_at_line", this, "_goto_script_line");
se->connect("go_to_help", this, "_help_class_goto");
se->connect("request_save_history", this, "_save_history");
+ se->connect("search_in_files_requested", this, "_on_find_in_files_requested");
//test for modification, maybe the script was not edited but was loaded
@@ -1787,6 +1802,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
se->goto_line(p_line - 1);
notify_script_changed(p_script);
+ _add_recent_script(p_script->get_path());
return true;
}
@@ -2304,6 +2320,7 @@ void ScriptEditor::_help_class_open(const String &p_class) {
_go_to_tab(tab_container->get_tab_count() - 1);
eh->go_to_class(p_class, 0);
eh->connect("go_to_help", this, "_help_class_goto");
+ _add_recent_script(p_class);
_update_script_names();
_save_layout();
}
@@ -2332,6 +2349,7 @@ void ScriptEditor::_help_class_goto(const String &p_desc) {
_go_to_tab(tab_container->get_tab_count() - 1);
eh->go_to_help(p_desc);
eh->connect("go_to_help", this, "_help_class_goto");
+ _add_recent_script(eh->get_class());
_update_script_names();
_save_layout();
}
@@ -2469,6 +2487,14 @@ void ScriptEditor::_open_script_request(const String &p_path) {
}
}
+int ScriptEditor::syntax_highlighters_func_count = 0;
+CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX];
+
+void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) {
+ ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX);
+ syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func;
+}
+
int ScriptEditor::script_editor_func_count = 0;
CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX];
@@ -2483,6 +2509,48 @@ void ScriptEditor::_script_changed() {
NodeDock::singleton->update_lists();
}
+void ScriptEditor::_on_find_in_files_requested(String text) {
+
+ find_in_files_dialog->set_search_text(text);
+ find_in_files_dialog->popup_centered_minsize();
+}
+
+void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) {
+
+ Ref<Resource> res = ResourceLoader::load(fpath);
+ edit(res);
+
+ ScriptEditorBase *seb = _get_current_editor();
+
+ ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(seb);
+ if (ste) {
+ ste->goto_line_selection(line_number - 1, begin, end);
+ }
+}
+
+void ScriptEditor::_start_find_in_files(bool with_replace) {
+
+ FindInFiles *f = find_in_files->get_finder();
+
+ f->set_search_text(find_in_files_dialog->get_search_text());
+ f->set_match_case(find_in_files_dialog->is_match_case());
+ f->set_whole_words(find_in_files_dialog->is_match_case());
+ f->set_folder(find_in_files_dialog->get_folder());
+ f->set_filter(find_in_files_dialog->get_filter());
+
+ find_in_files->set_with_replace(with_replace);
+ find_in_files->start_search();
+
+ find_in_files_button->set_pressed(true);
+ find_in_files->show();
+}
+
+void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) {
+
+ _test_script_times_on_disk();
+ _update_modified_scripts_for_external_editor();
+}
+
void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action);
@@ -2530,6 +2598,10 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input);
ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed);
ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
+ ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested);
+ ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files);
+ ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected);
+ ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files);
ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw);
@@ -2552,8 +2624,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
waiting_update_names = false;
pending_auto_reload = false;
auto_reload_running_scripts = false;
- members_overview_enabled = true;
- help_overview_enabled = true;
+ members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/open_scripts/show_members_overview");
+ help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
editor = p_editor;
VBoxContainer *main_container = memnew(VBoxContainer);
@@ -2586,11 +2658,13 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
members_overview = memnew(ItemList);
list_split->add_child(members_overview);
+ members_overview->set_allow_reselect(true);
members_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing
members_overview->set_v_size_flags(SIZE_EXPAND_FILL);
help_overview = memnew(ItemList);
list_split->add_child(help_overview);
+ help_overview->set_allow_reselect(true);
help_overview->set_custom_minimum_size(Size2(0, 90)); //need to give a bit of limit to avoid it from disappearing
help_overview->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -2622,7 +2696,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT | KEY_MASK_CMD | KEY_S), FILE_SAVE);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As..")), FILE_SAVE_AS);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT);
@@ -2651,7 +2725,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
script_search_menu = memnew(MenuButton);
menu_hb->add_child(script_search_menu);
script_search_menu->set_text(TTR("Search"));
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT);
script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option");
script_search_menu->hide();
@@ -2738,6 +2812,10 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(file_dialog);
file_dialog->connect("file_selected", this, "_file_dialog_action");
+ error_dialog = memnew(AcceptDialog);
+ add_child(error_dialog);
+ error_dialog->get_ok()->set_text(TTR("I see.."));
+
debugger = memnew(ScriptEditorDebugger(editor));
debugger->connect("goto_script_line", this, "_goto_script_line");
debugger->connect("show_debugger", this, "_show_debugger");
@@ -2785,13 +2863,26 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(help_index);
help_index->connect("open_class", this, "_help_class_open");
+ find_in_files_dialog = memnew(FindInFilesDialog);
+ find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
+ find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));
+ add_child(find_in_files_dialog);
+ find_in_files = memnew(FindInFilesPanel);
+ find_in_files_button = editor->add_bottom_panel_item(TTR("Search results"), find_in_files);
+ find_in_files_button->set_tooltip(TTR("Search in files"));
+ find_in_files->set_custom_minimum_size(Size2(0, 200));
+ find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected");
+ find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files");
+ find_in_files->hide();
+ find_in_files_button->hide();
+
history_pos = -1;
//debugger_gui->hide();
edit_pass = 0;
- trim_trailing_whitespace_on_save = false;
- convert_indent_on_save = false;
- use_space_indentation = false;
+ trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
+ convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
+ use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
ScriptServer::edit_request_func = _open_script_request;
@@ -2900,7 +2991,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
EDITOR_DEF("text_editor/open_scripts/script_temperature_history_size", 15);
EDITOR_DEF("text_editor/open_scripts/current_script_background_color", Color(1, 1, 1, 0.3));
EDITOR_DEF("text_editor/open_scripts/group_help_pages", true);
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path,None"));
EDITOR_DEF("text_editor/open_scripts/sort_scripts_by", 0);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path"));
EDITOR_DEF("text_editor/open_scripts/list_script_names_as", 0);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index e60e4cf8c0..9f37b18d7d 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -80,6 +80,9 @@ protected:
static void _bind_methods();
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0;
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0;
+
virtual void apply_code() = 0;
virtual Ref<Script> get_edited_script() const = 0;
virtual Vector<String> get_functions() = 0;
@@ -112,9 +115,12 @@ public:
ScriptEditorBase() {}
};
+typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)();
typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script);
class EditorScriptCodeCompletionCache;
+class FindInFilesDialog;
+class FindInFilesPanel;
class ScriptEditor : public PanelContainer {
@@ -165,6 +171,7 @@ class ScriptEditor : public PanelContainer {
enum ScriptSortBy {
SORT_BY_NAME,
SORT_BY_PATH,
+ SORT_BY_NONE
};
enum ScriptListName {
@@ -198,6 +205,7 @@ class ScriptEditor : public PanelContainer {
VSplitContainer *list_split;
TabContainer *tab_container;
EditorFileDialog *file_dialog;
+ AcceptDialog *error_dialog;
ConfirmationDialog *erase_tab_confirm;
ScriptCreateDialog *script_create_dialog;
ScriptEditorDebugger *debugger;
@@ -211,13 +219,21 @@ class ScriptEditor : public PanelContainer {
ToolButton *script_back;
ToolButton *script_forward;
+ FindInFilesDialog *find_in_files_dialog;
+ FindInFilesPanel *find_in_files;
+ Button *find_in_files_button;
+
enum {
- SCRIPT_EDITOR_FUNC_MAX = 32
+ SCRIPT_EDITOR_FUNC_MAX = 32,
+ SYNTAX_HIGHLIGHTER_FUNC_MAX = 32
};
static int script_editor_func_count;
static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX];
+ static int syntax_highlighters_func_count;
+ static CreateSyntaxHighlighterFunc syntax_highlighters_funcs[SYNTAX_HIGHLIGHTER_FUNC_MAX];
+
struct ScriptHistory {
Control *control;
@@ -227,8 +243,6 @@ class ScriptEditor : public PanelContainer {
Vector<ScriptHistory> history;
int history_pos;
- Vector<String> previous_scripts;
-
EditorHelpIndex *help_index;
void _tab_changed(int p_which);
@@ -250,7 +264,9 @@ class ScriptEditor : public PanelContainer {
void _update_recent_scripts();
void _open_recent_script(int p_idx);
- void _close_tab(int p_idx, bool p_save = true);
+ void _show_error_dialog(String p_path);
+
+ void _close_tab(int p_idx, bool p_save = true, bool p_history_back = true);
void _close_current_tab();
void _close_discard_current_tab(const String &p_str);
@@ -294,6 +310,8 @@ class ScriptEditor : public PanelContainer {
void _update_window_menu();
void _script_created(Ref<Script> p_script);
+ ScriptEditorBase *_get_current_editor() const;
+
void _save_layout();
void _editor_settings_changed();
void _autosave_scripts();
@@ -349,6 +367,11 @@ class ScriptEditor : public PanelContainer {
Ref<Script> _get_current_script();
Array _get_open_scripts() const;
+ void _on_find_in_files_requested(String text);
+ void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end);
+ void _start_find_in_files(bool with_replace);
+ void _on_find_in_files_modified_files(PoolStringArray paths);
+
static void _open_script_request(const String &p_path);
static ScriptEditor *script_editor;
@@ -397,7 +420,9 @@ public:
ScriptEditorDebugger *get_debugger() { return debugger; }
void set_live_auto_reload_running_scripts(bool p_enabled);
+ static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func);
static void register_create_script_editor_function(CreateScriptEditorFunc p_func);
+
ScriptEditor(EditorNode *p_editor);
~ScriptEditor();
};
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index dffb98e488..45f5e667fa 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -349,7 +349,12 @@ void ScriptTextEditor::_convert_case(CaseStyle p_case) {
int end_col = te->get_selection_to_column();
for (int i = begin; i <= end; i++) {
- String new_line = te->get_line(i);
+ int len = te->get_line(i).length();
+ if (i == end)
+ len -= len - end_col;
+ if (i == begin)
+ len -= begin_col;
+ String new_line = te->get_line(i).substr(i == begin ? begin_col : 0, len);
switch (p_case) {
case UPPER: {
@@ -364,10 +369,10 @@ void ScriptTextEditor::_convert_case(CaseStyle p_case) {
}
if (i == begin) {
- new_line = te->get_line(i).left(begin_col) + new_line.right(begin_col);
+ new_line = te->get_line(i).left(begin_col) + new_line;
}
if (i == end) {
- new_line = new_line.left(end_col) + te->get_line(i).right(end_col);
+ new_line = new_line + te->get_line(i).right(end_col);
}
te->set_line(i, new_line);
}
@@ -519,10 +524,19 @@ void ScriptTextEditor::tag_saved_version() {
void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
TextEdit *tx = code_editor->get_text_edit();
+ tx->deselect();
tx->unfold_line(p_line);
tx->call_deferred("cursor_set_line", p_line);
}
+void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
+ TextEdit *tx = code_editor->get_text_edit();
+ tx->unfold_line(p_line);
+ tx->call_deferred("cursor_set_line", p_line);
+ tx->call_deferred("cursor_set_column", p_begin);
+ tx->select(p_line, p_begin, p_line, p_end);
+}
+
void ScriptTextEditor::ensure_focus() {
code_editor->get_text_edit()->grab_focus();
@@ -567,6 +581,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
ERR_FAIL_COND(!script.is_null());
script = p_script;
+ _set_theme_for_script();
code_editor->get_text_edit()->set_text(script->get_source_code());
code_editor->get_text_edit()->clear_undo_history();
@@ -574,8 +589,6 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
emit_signal("name_changed");
code_editor->update_line_and_column();
-
- _set_theme_for_script();
}
void ScriptTextEditor::_validate_script() {
@@ -733,6 +746,8 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
_goto_line(p_row);
+ result.class_name = result.class_name.trim_prefix("_");
+
switch (result.type) {
case ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION: {
@@ -783,6 +798,26 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
emit_signal("go_to_help", "class_method:" + result.class_name + ":" + result.class_member);
} break;
+ case ScriptLanguage::LookupResult::RESULT_CLASS_ENUM: {
+
+ StringName cname = result.class_name;
+ StringName success;
+ while (true) {
+ success = ClassDB::get_integer_constant_enum(cname, result.class_member, true);
+ if (success != StringName()) {
+ result.class_name = cname;
+ cname = ClassDB::get_parent_class(cname);
+ } else {
+ break;
+ }
+ }
+
+ emit_signal("go_to_help", "class_enum:" + result.class_name + ":" + result.class_member);
+
+ } break;
+ case ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE: {
+ emit_signal("go_to_help", "class_global:" + result.class_name + ":" + result.class_member);
+ } break;
}
}
}
@@ -934,13 +969,27 @@ void ScriptTextEditor::_edit_option(int p_op) {
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
-
tx->begin_complex_operation();
- int line = tx->cursor_get_line();
- tx->set_line(tx->cursor_get_line(), "");
- tx->backspace_at_cursor();
- tx->unfold_line(line);
- tx->cursor_set_line(line);
+ if (tx->is_selection_active()) {
+ int to_line = tx->get_selection_to_line();
+ int from_line = tx->get_selection_from_line();
+ int count = Math::abs(to_line - from_line) + 1;
+ while (count) {
+ tx->set_line(tx->cursor_get_line(), "");
+ tx->backspace_at_cursor();
+ count--;
+ if (count)
+ tx->unfold_line(from_line);
+ }
+ tx->cursor_set_line(from_line - 1);
+ tx->deselect();
+ } else {
+ int line = tx->cursor_get_line();
+ tx->set_line(tx->cursor_get_line(), "");
+ tx->backspace_at_cursor();
+ tx->unfold_line(line);
+ tx->cursor_set_line(line);
+ }
tx->end_complex_operation();
} break;
case EDIT_CLONE_DOWN: {
@@ -960,6 +1009,10 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
int next_line = to_line + 1;
+ if (to_line >= tx->get_line_count() - 1) {
+ tx->set_line(to_line, tx->get_line(to_line) + "\n");
+ }
+
tx->begin_complex_operation();
for (int i = from_line; i <= to_line; i++) {
@@ -1134,6 +1187,15 @@ void ScriptTextEditor::_edit_option(int p_op) {
code_editor->get_find_replace_bar()->popup_replace();
} break;
+ case SEARCH_IN_FILES: {
+
+ String selected_text = code_editor->get_text_edit()->get_selection_text();
+
+ // Yep, because it doesn't make sense to instance this dialog for every single script open...
+ // So this will be delegated to the ScriptEditor
+ emit_signal("search_in_files_requested", selected_text);
+
+ } break;
case SEARCH_LOCATE_FUNCTION: {
quick_open->popup(get_functions());
@@ -1225,11 +1287,26 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
}
+void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+ highlighters[p_highlighter->get_name()] = p_highlighter;
+ highlighter_menu->get_popup()->add_item(p_highlighter->get_name());
+}
+
+void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+ TextEdit *te = code_editor->get_text_edit();
+ te->_set_syntax_highlighting(p_highlighter);
+}
+
+void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
+ set_syntax_highlighter(highlighters[highlighter_menu->get_popup()->get_item_text(p_idx)]);
+}
+
void ScriptTextEditor::_bind_methods() {
ClassDB::bind_method("_validate_script", &ScriptTextEditor::_validate_script);
ClassDB::bind_method("_load_theme_settings", &ScriptTextEditor::_load_theme_settings);
ClassDB::bind_method("_breakpoint_toggled", &ScriptTextEditor::_breakpoint_toggled);
+ ClassDB::bind_method("_change_syntax_highlighter", &ScriptTextEditor::_change_syntax_highlighter);
ClassDB::bind_method("_edit_option", &ScriptTextEditor::_edit_option);
ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line);
ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol);
@@ -1283,12 +1360,9 @@ Variant ScriptTextEditor::get_drag_data_fw(const Point2 &p_point, Control *p_fro
bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
Dictionary d = p_data;
- if (d.has("type") &&
- (
-
- String(d["type"]) == "resource" ||
- String(d["type"]) == "files" ||
- String(d["type"]) == "nodes")) {
+ if (d.has("type") && (String(d["type"]) == "resource" ||
+ String(d["type"]) == "files" ||
+ String(d["type"]) == "nodes")) {
return true;
}
@@ -1329,6 +1403,10 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
Dictionary d = p_data;
+ TextEdit *te = code_editor->get_text_edit();
+ int row, col;
+ te->_get_mouse_pos(p_point, row, col);
+
if (d.has("type") && String(d["type"]) == "resource") {
Ref<Resource> res = d["resource"];
@@ -1341,7 +1419,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
return;
}
- code_editor->get_text_edit()->insert_text_at_cursor(res->get_path());
+ te->cursor_set_line(row);
+ te->cursor_set_column(col);
+ te->insert_text_at_cursor(res->get_path());
}
if (d.has("type") && String(d["type"]) == "files") {
@@ -1356,7 +1436,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
text_to_drop += "\"" + String(files[i]).c_escape() + "\"";
}
- code_editor->get_text_edit()->insert_text_at_cursor(text_to_drop);
+ te->cursor_set_line(row);
+ te->cursor_set_column(col);
+ te->insert_text_at_cursor(text_to_drop);
}
if (d.has("type") && String(d["type"]) == "nodes") {
@@ -1385,7 +1467,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
text_to_drop += "\"" + path.c_escape() + "\"";
}
- code_editor->get_text_edit()->insert_text_at_cursor(text_to_drop);
+ te->cursor_set_line(row);
+ te->cursor_set_column(col);
+ te->insert_text_at_cursor(text_to_drop);
}
}
@@ -1565,16 +1649,12 @@ ScriptTextEditor::ScriptTextEditor() {
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES);
edit_menu->get_popup()->add_separator();
-#ifdef OSX_ENABLED
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE);
-#else
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE);
-#endif
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS);
@@ -1603,6 +1683,8 @@ ScriptTextEditor::ScriptTextEditor() {
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
search_menu->get_popup()->add_separator();
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES);
+ search_menu->get_popup()->add_separator();
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION);
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
search_menu->get_popup()->add_separator();
@@ -1612,6 +1694,14 @@ ScriptTextEditor::ScriptTextEditor() {
edit_hb->add_child(edit_menu);
+ highlighters["Standard"] = NULL;
+
+ highlighter_menu = memnew(MenuButton);
+ highlighter_menu->set_text(TTR("Syntax Highlighter"));
+ highlighter_menu->get_popup()->add_item("Standard");
+ highlighter_menu->get_popup()->connect("id_pressed", this, "_change_syntax_highlighter");
+ edit_hb->add_child(highlighter_menu);
+
quick_open = memnew(ScriptEditorQuickOpen);
add_child(quick_open);
quick_open->connect("goto_line", this, "_goto_line");
@@ -1645,13 +1735,14 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), 0);
ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0);
ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K);
- ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B);
ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F);
ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), 0);
ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0);
#ifdef OSX_ENABLED
+ ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C);
ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE);
#else
+ ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B);
ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE);
#endif
ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CTRL | KEY_MASK_ALT | KEY_T);
@@ -1668,13 +1759,15 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Convert To Lowercase"), KEY_MASK_SHIFT | KEY_F3);
ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F2);
- ED_SHORTCUT("script_text_editor/find", TTR("Find.."), KEY_MASK_CMD | KEY_F);
+ ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F);
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3);
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
- ED_SHORTCUT("script_text_editor/replace", TTR("Replace.."), KEY_MASK_CMD | KEY_R);
+ ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R);
+
+ ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);
- ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function.."), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_F);
- ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD | KEY_L);
+ ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
+ ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line..."), KEY_MASK_CMD | KEY_L);
ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1);
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 22e8fbce25..a93e1a6fa8 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -49,6 +49,7 @@ class ScriptTextEditor : public ScriptEditorBase {
HBoxContainer *edit_hb;
MenuButton *edit_menu;
+ MenuButton *highlighter_menu;
MenuButton *search_menu;
PopupMenu *context_menu;
@@ -105,6 +106,7 @@ class ScriptTextEditor : public ScriptEditorBase {
SEARCH_REPLACE,
SEARCH_LOCATE_FUNCTION,
SEARCH_GOTO_LINE,
+ SEARCH_IN_FILES,
DEBUG_TOGGLE_BREAKPOINT,
DEBUG_REMOVE_ALL_BREAKPOINTS,
DEBUG_GOTO_NEXT_BREAKPOINT,
@@ -125,6 +127,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ Map<String, SyntaxHighlighter *> highlighters;
+ void _change_syntax_highlighter(int p_idx);
+
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
@@ -145,6 +150,9 @@ protected:
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+
virtual void apply_code();
virtual Ref<Script> get_edited_script() const;
virtual Vector<String> get_functions();
@@ -163,6 +171,7 @@ public:
virtual void tag_saved_version();
virtual void goto_line(int p_line, bool p_with_error = false);
+ void goto_line_selection(int p_line, int p_begin, int p_end);
virtual void reload(bool p_soft);
virtual void get_breakpoints(List<int> *p_breakpoints);
diff --git a/editor/plugins/shader_graph_editor_plugin.cpp b/editor/plugins/shader_graph_editor_plugin.cpp
index 59085c203f..1a9d980feb 100644
--- a/editor/plugins/shader_graph_editor_plugin.cpp
+++ b/editor/plugins/shader_graph_editor_plugin.cpp
@@ -1478,7 +1478,7 @@ void ShaderGraphView::_create_node(int p_id) {
case ShaderGraph::NODE_XFORM_CONST: {
gn->set_title("XForm");
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_xform_const_changed",varray(p_id,edit));
gn->add_child(edit);
gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
@@ -2289,7 +2289,7 @@ void ShaderGraphView::_create_node(int p_id) {
le->set_text(graph->input_node_get_name(type,p_id));
le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_xform_input_changed",varray(p_id,edit));
gn->add_child(edit);
gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
@@ -2310,7 +2310,7 @@ void ShaderGraphView::_create_node(int p_id) {
tex->set_mouse_filter(MOUSE_FILTER_PASS);
tex->set_texture(graph->texture_input_node_get_value(type,p_id));
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_tex_edited",varray(p_id,edit));
gn->add_child(edit);
@@ -2345,7 +2345,7 @@ void ShaderGraphView::_create_node(int p_id) {
le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_cube_edited",varray(p_id,edit));
gn->add_child(edit);
@@ -2769,8 +2769,6 @@ void ShaderGraphEditor::_popup_requested(const Vector2 &p_position)
popup->set_global_position(p_position);
popup->set_size( Size2( 200, 0) );
popup->popup();
- popup->call_deferred("grab_click_focus");
- popup->set_invalidate_click_until_motion();
}
void ShaderGraphEditor::_notification(int p_what) {
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
new file mode 100644
index 0000000000..e372f792d6
--- /dev/null
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -0,0 +1,120 @@
+#include "skeleton_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "scene/2d/mesh_instance_2d.h"
+#include "scene/gui/box_container.h"
+#include "thirdparty/misc/clipper.hpp"
+
+void Skeleton2DEditor::_node_removed(Node *p_node) {
+
+ if (p_node == node) {
+ node = NULL;
+ options->hide();
+ }
+}
+
+void Skeleton2DEditor::edit(Skeleton2D *p_sprite) {
+
+ node = p_sprite;
+}
+
+void Skeleton2DEditor::_menu_option(int p_option) {
+
+ if (!node) {
+ return;
+ }
+
+ switch (p_option) {
+ case MENU_OPTION_MAKE_REST: {
+
+ if (node->get_bone_count() == 0) {
+ err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Create Rest Pose from Bones");
+ for (int i = 0; i < node->get_bone_count(); i++) {
+ Bone2D *bone = node->get_bone(i);
+ ur->add_do_method(bone, "set_rest", bone->get_transform());
+ ur->add_undo_method(bone, "set_rest", bone->get_rest());
+ }
+ ur->commit_action();
+
+ } break;
+ case MENU_OPTION_SET_REST: {
+ if (node->get_bone_count() == 0) {
+ err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action("Set Rest Pose to Bones");
+ for (int i = 0; i < node->get_bone_count(); i++) {
+ Bone2D *bone = node->get_bone(i);
+ ur->add_do_method(bone, "set_transform", bone->get_rest());
+ ur->add_undo_method(bone, "set_transform", bone->get_transform());
+ }
+ ur->commit_action();
+
+ } break;
+ }
+}
+
+void Skeleton2DEditor::_bind_methods() {
+
+ ClassDB::bind_method("_menu_option", &Skeleton2DEditor::_menu_option);
+}
+
+Skeleton2DEditor::Skeleton2DEditor() {
+
+ options = memnew(MenuButton);
+
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->set_text(TTR("Skeleton2D"));
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton2D", "EditorIcons"));
+
+ options->get_popup()->add_item(TTR("Make Rest Pose (From Bones)"), MENU_OPTION_MAKE_REST);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("Set Bones to Rest Pose"), MENU_OPTION_SET_REST);
+
+ options->get_popup()->connect("id_pressed", this, "_menu_option");
+
+ err_dialog = memnew(AcceptDialog);
+ add_child(err_dialog);
+}
+
+void Skeleton2DEditorPlugin::edit(Object *p_object) {
+
+ sprite_editor->edit(Object::cast_to<Skeleton2D>(p_object));
+}
+
+bool Skeleton2DEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("Skeleton2D");
+}
+
+void Skeleton2DEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ sprite_editor->options->show();
+ } else {
+
+ sprite_editor->options->hide();
+ sprite_editor->edit(NULL);
+ }
+}
+
+Skeleton2DEditorPlugin::Skeleton2DEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ sprite_editor = memnew(Skeleton2DEditor);
+ editor->get_viewport()->add_child(sprite_editor);
+ make_visible(false);
+
+ //sprite_editor->options->hide();
+}
+
+Skeleton2DEditorPlugin::~Skeleton2DEditorPlugin() {
+}
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
new file mode 100644
index 0000000000..bbe2a3a6f2
--- /dev/null
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -0,0 +1,55 @@
+#ifndef SKELETON_2D_EDITOR_PLUGIN_H
+#define SKELETON_2D_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/2d/skeleton_2d.h"
+#include "scene/gui/spin_box.h"
+
+class Skeleton2DEditor : public Control {
+
+ GDCLASS(Skeleton2DEditor, Control);
+
+ enum Menu {
+ MENU_OPTION_MAKE_REST,
+ MENU_OPTION_SET_REST,
+ };
+
+ Skeleton2D *node;
+
+ MenuButton *options;
+ AcceptDialog *err_dialog;
+
+ void _menu_option(int p_option);
+
+ //void _create_uv_lines();
+ friend class Skeleton2DEditorPlugin;
+
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+public:
+ void edit(Skeleton2D *p_sprite);
+ Skeleton2DEditor();
+};
+
+class Skeleton2DEditorPlugin : public EditorPlugin {
+
+ GDCLASS(Skeleton2DEditorPlugin, EditorPlugin);
+
+ Skeleton2DEditor *sprite_editor;
+ EditorNode *editor;
+
+public:
+ virtual String get_name() const { return "Skeleton2D"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ Skeleton2DEditorPlugin(EditorNode *p_node);
+ ~Skeleton2DEditorPlugin();
+};
+
+#endif // SKELETON_2D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp
new file mode 100644
index 0000000000..c41e3b546f
--- /dev/null
+++ b/editor/plugins/skeleton_editor_plugin.cpp
@@ -0,0 +1,189 @@
+/*************************************************************************/
+/* skeleton_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "skeleton_editor_plugin.h"
+#include "scene/3d/collision_shape.h"
+#include "scene/3d/physics_body.h"
+#include "scene/3d/physics_joint.h";
+#include "scene/resources/capsule_shape.h"
+#include "scene/resources/sphere_shape.h"
+#include "spatial_editor_plugin.h"
+
+void SkeletonEditor::_on_click_option(int p_option) {
+ if (!skeleton) {
+ return;
+ }
+
+ switch (p_option) {
+ case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
+ create_physical_skeleton();
+ } break;
+ }
+}
+
+void SkeletonEditor::create_physical_skeleton() {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner();
+
+ const int bc = skeleton->get_bone_count();
+
+ if (!bc) {
+ return;
+ }
+
+ Vector<BoneInfo> bones_infos;
+ bones_infos.resize(bc);
+
+ for (int bone_id = 0; bc > bone_id; ++bone_id) {
+
+ const int parent = skeleton->get_bone_parent(bone_id);
+ const int parent_parent = skeleton->get_bone_parent(parent);
+
+ if (parent < 0) {
+
+ bones_infos[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
+
+ } else {
+
+ bones_infos[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
+
+ /// create physical bone on parent
+ if (!bones_infos[parent].physical_bone) {
+
+ bones_infos[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
+
+ ur->create_action(TTR("Create physical bones"));
+ ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone);
+ ur->add_do_reference(bones_infos[parent].physical_bone);
+ ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone);
+ ur->commit_action();
+
+ bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent));
+ bones_infos[parent].physical_bone->set_owner(owner);
+ bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
+
+ /// Create joint between parent of parent
+ if (-1 != parent_parent) {
+
+ bones_infos[parent].physical_bone->set_joint_type(PhysicalBone::JOINT_TYPE_PIN);
+ }
+ }
+ }
+ }
+}
+
+PhysicalBone *SkeletonEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) {
+
+ real_t half_height(skeleton->get_bone_rest(bone_child_id).origin.length() * 0.5);
+ real_t radius(half_height * 0.2);
+
+ CapsuleShape *bone_shape_capsule = memnew(CapsuleShape);
+ bone_shape_capsule->set_height((half_height - radius) * 2);
+ bone_shape_capsule->set_radius(radius);
+
+ CollisionShape *bone_shape = memnew(CollisionShape);
+ bone_shape->set_shape(bone_shape_capsule);
+
+ Transform body_transform;
+ body_transform.origin = Vector3(0, 0, -half_height);
+
+ Transform joint_transform;
+ joint_transform.origin = Vector3(0, 0, half_height);
+
+ PhysicalBone *physical_bone = memnew(PhysicalBone);
+ physical_bone->add_child(bone_shape);
+ physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id));
+ physical_bone->set_body_offset(body_transform);
+ physical_bone->set_joint_offset(joint_transform);
+ return physical_bone;
+}
+
+void SkeletonEditor::edit(Skeleton *p_node) {
+ skeleton = p_node;
+}
+
+void SkeletonEditor::_node_removed(Node *p_node) {
+
+ if (p_node == skeleton) {
+ skeleton = NULL;
+ options->hide();
+ }
+}
+
+void SkeletonEditor::_bind_methods() {
+ ClassDB::bind_method("_on_click_option", &SkeletonEditor::_on_click_option);
+}
+
+SkeletonEditor::SkeletonEditor() {
+ options = memnew(MenuButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->set_text(TTR("Skeleton"));
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton", "EditorIcons"));
+
+ options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
+
+ options->get_popup()->connect("id_pressed", this, "_on_click_option");
+ options->hide();
+}
+
+SkeletonEditor::~SkeletonEditor() {
+ SpatialEditor::get_singleton()->remove_child(options);
+ memdelete(options);
+}
+
+void SkeletonEditorPlugin::edit(Object *p_object) {
+ skeleton_editor->edit(Object::cast_to<Skeleton>(p_object));
+}
+
+bool SkeletonEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("Skeleton");
+}
+
+void SkeletonEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ skeleton_editor->options->show();
+ } else {
+
+ skeleton_editor->options->hide();
+ skeleton_editor->edit(NULL);
+ }
+}
+
+SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ skeleton_editor = memnew(SkeletonEditor);
+ editor->get_viewport()->add_child(skeleton_editor);
+}
+
+SkeletonEditorPlugin::~SkeletonEditorPlugin() {
+ editor->get_viewport()->remove_child(skeleton_editor);
+ memdelete(skeleton_editor);
+}
diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h
new file mode 100644
index 0000000000..b9bdf91902
--- /dev/null
+++ b/editor/plugins/skeleton_editor_plugin.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* skeleton_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SKELETON_EDITOR_PLUGIN_H
+#define SKELETON_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/skeleton.h"
+
+class PhysicalBone;
+class Joint;
+
+class SkeletonEditor : public Node {
+ GDCLASS(SkeletonEditor, Node);
+
+ enum Menu {
+ MENU_OPTION_CREATE_PHYSICAL_SKELETON
+ };
+
+ struct BoneInfo {
+ PhysicalBone *physical_bone;
+ Transform relative_rest; // Relative to skeleton node
+ BoneInfo() :
+ physical_bone(NULL) {}
+ };
+
+ Skeleton *skeleton;
+
+ MenuButton *options;
+
+ void _on_click_option(int p_option);
+
+ friend class SkeletonEditorPlugin;
+
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+ void create_physical_skeleton();
+ PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
+
+public:
+ void edit(Skeleton *p_mesh);
+
+ SkeletonEditor();
+ ~SkeletonEditor();
+};
+
+class SkeletonEditorPlugin : public EditorPlugin {
+
+ GDCLASS(SkeletonEditorPlugin, EditorPlugin);
+
+ EditorNode *editor;
+ SkeletonEditor *skeleton_editor;
+
+public:
+ virtual String get_name() const { return "Skeleton"; }
+ virtual bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ SkeletonEditorPlugin(EditorNode *p_node);
+ ~SkeletonEditorPlugin();
+};
+
+#endif // SKELETON_EDITOR_PLUGIN_H
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 5e8eb06556..5b713ef3c4 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -72,6 +72,14 @@
#define MIN_FOV 0.01
#define MAX_FOV 179
+#ifdef TOOLS_ENABLED
+#define get_global_gizmo_transform get_global_gizmo_transform
+#define get_local_gizmo_transform get_local_gizmo_transform
+#else
+#define get_global_gizmo_transform get_global_transform
+#define get_local_gizmo_transform get_transform
+#endif
+
void SpatialEditorViewport::_update_camera(float p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
@@ -318,6 +326,9 @@ void SpatialEditorViewport::_select(Spatial *p_node, bool p_append, bool p_singl
editor_selection->clear();
editor_selection->add_node(p_node);
+ if (Engine::get_singleton()->is_editor_hint())
+ editor->call("edit_node", p_node);
+
} else {
if (editor_selection->is_selected(p_node) && p_single) {
@@ -581,8 +592,8 @@ void SpatialEditorViewport::_compute_edit(const Point2 &p_point) {
if (!se)
continue;
- se->original = se->sp->get_global_transform();
- se->original_local = se->sp->get_transform();
+ se->original = se->sp->get_global_gizmo_transform();
+ se->original_local = se->sp->get_local_gizmo_transform();
}
}
@@ -883,8 +894,6 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
selection_menu->set_global_position(b->get_global_position());
selection_menu->popup();
- selection_menu->call_deferred("grab_click_focus");
- selection_menu->set_invalidate_click_until_motion();
}
}
@@ -1183,7 +1192,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (!se)
continue;
- undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_transform());
+ undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
undo_redo->add_undo_method(sp, "set_global_transform", se->original);
}
undo_redo->commit_action();
@@ -1904,8 +1913,13 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con
real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
+ bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis");
- cursor.x_rot += p_relative.y * radians_per_pixel;
+ if (invert_y_axis) {
+ cursor.x_rot -= p_relative.y * radians_per_pixel;
+ } else {
+ cursor.x_rot += p_relative.y * radians_per_pixel;
+ }
cursor.y_rot += p_relative.x * radians_per_pixel;
if (cursor.x_rot > Math_PI / 2.0)
cursor.x_rot = Math_PI / 2.0;
@@ -1922,11 +1936,16 @@ void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, cons
if (!orthogonal) {
real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
+ bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis");
// Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
Transform prev_camera_transform = to_camera_transform(cursor);
- cursor.x_rot += p_relative.y * radians_per_pixel;
+ if (invert_y_axis) {
+ cursor.x_rot -= p_relative.y * radians_per_pixel;
+ } else {
+ cursor.x_rot += p_relative.y * radians_per_pixel;
+ }
cursor.y_rot += p_relative.x * radians_per_pixel;
if (cursor.x_rot > Math_PI / 2.0)
cursor.x_rot = Math_PI / 2.0;
@@ -2139,7 +2158,7 @@ void SpatialEditorViewport::_notification(int p_what) {
se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
}
- Transform t = sp->get_global_transform();
+ Transform t = sp->get_global_gizmo_transform();
t.translate(se->aabb.position);
// apply AABB scaling before item's global transform
@@ -2492,7 +2511,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
xform.scale_basis(sp->get_scale());
undo_redo->add_do_method(sp, "set_global_transform", xform);
- undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+ undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
}
undo_redo->commit_action();
} break;
@@ -2950,7 +2969,7 @@ void SpatialEditorViewport::focus_selection() {
if (!se)
continue;
- center += sp->get_global_transform().origin;
+ center += sp->get_global_gizmo_transform().origin;
count++;
}
@@ -3032,7 +3051,7 @@ AABB SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, c
MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(child);
if (mesh_instance) {
AABB mesh_instance_bounds = mesh_instance->get_aabb();
- mesh_instance_bounds.position += mesh_instance->get_global_transform().origin - p_parent->get_global_transform().origin;
+ mesh_instance_bounds.position += mesh_instance->get_global_gizmo_transform().origin - p_parent->get_global_gizmo_transform().origin;
bounds.merge_with(mesh_instance_bounds);
}
bounds = _calculate_spatial_bounds(child, bounds);
@@ -3110,7 +3129,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
if (!scene.is_valid()) { // invalid scene
return false;
} else {
- instanced_scene = scene->instance();
+ instanced_scene = scene->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
}
}
}
@@ -3143,7 +3162,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
Transform global_transform;
Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
if (parent_spatial)
- global_transform = parent_spatial->get_global_transform();
+ global_transform = parent_spatial->get_global_gizmo_transform();
global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point));
@@ -3272,7 +3291,7 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p
}
}
if (list.size() != 1) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation requires a single selected node."));
accept->popup_centered_minsize();
_remove_preview();
@@ -3340,14 +3359,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
- view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
+ view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
+ view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
@@ -3776,7 +3795,8 @@ void SpatialEditor::update_transform_gizmo() {
if (!se)
continue;
- Transform xf = se->sp->get_global_transform();
+ Transform xf = se->sp->get_global_gizmo_transform();
+
if (first) {
center.position = xf.origin;
first = false;
@@ -3827,9 +3847,6 @@ Object *SpatialEditor::_get_editor_data(Object *p_what) {
si->sbox_instance = VisualServer::get_singleton()->instance_create2(selection_box->get_rid(), sp->get_world()->get_scenario());
VS::get_singleton()->instance_geometry_set_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF);
- if (Engine::get_singleton()->is_editor_hint())
- editor->call("edit_node", sp);
-
return si;
}
@@ -4046,7 +4063,7 @@ void SpatialEditor::_xform_dialog_action() {
bool post = xform_type->get_selected() > 0;
- Transform tr = sp->get_global_transform();
+ Transform tr = sp->get_global_gizmo_transform();
if (post)
tr = tr * t;
else {
@@ -4056,7 +4073,7 @@ void SpatialEditor::_xform_dialog_action() {
}
undo_redo->add_do_method(sp, "set_global_transform", tr);
- undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+ undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
}
undo_redo->commit_action();
}
@@ -4282,67 +4299,26 @@ void SpatialEditor::_init_indicators() {
indicator_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
indicator_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
- PoolVector<Color> grid_colors[3];
- PoolVector<Vector3> grid_points[3];
Vector<Color> origin_colors;
Vector<Vector3> origin_points;
- Color grid_color = EditorSettings::get_singleton()->get("editors/3d/grid_color");
-
for (int i = 0; i < 3; i++) {
Vector3 axis;
axis[i] = 1;
- Vector3 axis_n1;
- axis_n1[(i + 1) % 3] = 1;
- Vector3 axis_n2;
- axis_n2[(i + 2) % 3] = 1;
+
+ grid_enable[i] = false;
+ grid_visible[i] = false;
origin_colors.push_back(Color(axis.x, axis.y, axis.z));
origin_colors.push_back(Color(axis.x, axis.y, axis.z));
origin_points.push_back(axis * 4096);
origin_points.push_back(axis * -4096);
-#define ORIGIN_GRID_SIZE 50
-
- for (int j = -ORIGIN_GRID_SIZE; j <= ORIGIN_GRID_SIZE; j++) {
-
- Vector3 p1 = axis_n1 * j + axis_n2 * -ORIGIN_GRID_SIZE;
- Vector3 p1_dest = p1 * (-axis_n2 + axis_n1);
- Vector3 p2 = axis_n2 * j + axis_n1 * -ORIGIN_GRID_SIZE;
- Vector3 p2_dest = p2 * (-axis_n1 + axis_n2);
-
- Color line_color = grid_color;
- if (j == 0) {
- continue;
- } else if (j % 10 == 0) {
- line_color *= 1.5;
- }
-
- grid_points[i].push_back(p1);
- grid_points[i].push_back(p1_dest);
- grid_colors[i].push_back(line_color);
- grid_colors[i].push_back(line_color);
-
- grid_points[i].push_back(p2);
- grid_points[i].push_back(p2_dest);
- grid_colors[i].push_back(line_color);
- grid_colors[i].push_back(line_color);
- }
+ }
- grid[i] = VisualServer::get_singleton()->mesh_create();
- Array d;
- d.resize(VS::ARRAY_MAX);
- d[VisualServer::ARRAY_VERTEX] = grid_points[i];
- d[VisualServer::ARRAY_COLOR] = grid_colors[i];
- VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d);
- VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid());
- grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario());
+ grid_enable[1] = true;
+ grid_visible[1] = true;
- grid_visible[i] = false;
- grid_enable[i] = false;
- VisualServer::get_singleton()->instance_set_visible(grid_instance[i], false);
- VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
- VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << SpatialEditorViewport::GIZMO_GRID_LAYER);
- }
+ _init_grid();
origin = VisualServer::get_singleton()->mesh_create();
Array d;
@@ -4358,9 +4334,6 @@ void SpatialEditor::_init_indicators() {
VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF);
- VisualServer::get_singleton()->instance_set_visible(grid_instance[1], true);
- grid_enable[1] = true;
- grid_visible[1] = true;
grid_enabled = true;
last_grid_snap = 1;
}
@@ -4629,10 +4602,72 @@ void SpatialEditor::_init_indicators() {
_generate_selection_box();
}
+void SpatialEditor::_init_grid() {
+
+ PoolVector<Color> grid_colors[3];
+ PoolVector<Vector3> grid_points[3];
+
+ Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color");
+ Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color");
+ int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size");
+ int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps");
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 axis;
+ axis[i] = 1;
+ Vector3 axis_n1;
+ axis_n1[(i + 1) % 3] = 1;
+ Vector3 axis_n2;
+ axis_n2[(i + 2) % 3] = 1;
+
+ for (int j = -grid_size; j <= grid_size; j++) {
+ Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size;
+ Vector3 p1_dest = p1 * (-axis_n2 + axis_n1);
+ Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size;
+ Vector3 p2_dest = p2 * (-axis_n1 + axis_n2);
+
+ Color line_color = secondary_grid_color;
+ if (j == 0) {
+ continue;
+ } else if (j % primary_grid_steps == 0) {
+ line_color = primary_grid_color;
+ }
+
+ grid_points[i].push_back(p1);
+ grid_points[i].push_back(p1_dest);
+ grid_colors[i].push_back(line_color);
+ grid_colors[i].push_back(line_color);
+
+ grid_points[i].push_back(p2);
+ grid_points[i].push_back(p2_dest);
+ grid_colors[i].push_back(line_color);
+ grid_colors[i].push_back(line_color);
+ }
+
+ grid[i] = VisualServer::get_singleton()->mesh_create();
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[VisualServer::ARRAY_VERTEX] = grid_points[i];
+ d[VisualServer::ARRAY_COLOR] = grid_colors[i];
+ VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d);
+ VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid());
+ grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario());
+
+ VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]);
+ VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
+ VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << SpatialEditorViewport::GIZMO_GRID_LAYER);
+ }
+}
+
void SpatialEditor::_finish_indicators() {
VisualServer::get_singleton()->free(origin_instance);
VisualServer::get_singleton()->free(origin);
+
+ _finish_grid();
+}
+
+void SpatialEditor::_finish_grid() {
for (int i = 0; i < 3; i++) {
VisualServer::get_singleton()->free(grid_instance[i]);
VisualServer::get_singleton()->free(grid[i]);
@@ -4674,6 +4709,8 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack())
return;
+ snap_key_enabled = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
+
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
@@ -4770,6 +4807,10 @@ void SpatialEditor::_notification(int p_what) {
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons"));
+
+ // Update grid color by rebuilding grid.
+ _finish_grid();
+ _init_grid();
}
}
@@ -4778,6 +4819,11 @@ void SpatialEditor::add_control_to_menu_panel(Control *p_control) {
hbc_menu->add_child(p_control);
}
+void SpatialEditor::remove_control_from_menu_panel(Control *p_control) {
+
+ hbc_menu->remove_child(p_control);
+}
+
void SpatialEditor::set_can_preview(Camera *p_preview) {
for (int i = 0; i < 4; i++) {
@@ -4936,6 +4982,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
editor_selection->add_editor_plugin(this);
snap_enabled = false;
+ snap_key_enabled = false;
tool_mode = TOOL_MODE_SELECT;
hbc_menu = memnew(HBoxContainer);
@@ -5045,8 +5092,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Tool Rotate"), KEY_E);
ED_SHORTCUT("spatial_editor/tool_scale", TTR("Tool Scale"), KEY_R);
- ED_SHORTCUT("spatial_editor/display_wireframe", TTR("Display Wireframe"), KEY_Z);
-
ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F);
PopupMenu *p;
@@ -5056,9 +5101,9 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
hbc_menu->add_child(transform_menu);
p = transform_menu->get_popup();
- p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap..")), MENU_TRANSFORM_CONFIGURE_SNAP);
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP);
p->add_separator();
- p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog..")), MENU_TRANSFORM_DIALOG);
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG);
p->connect("id_pressed", this, "_menu_item_pressed");
@@ -5072,12 +5117,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
accept = memnew(AcceptDialog);
editor->get_gui_base()->add_child(accept);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index e12f7affb7..7736db67b1 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -505,6 +505,7 @@ private:
ConfirmationDialog *settings_dialog;
bool snap_enabled;
+ bool snap_key_enabled;
LineEdit *snap_translate;
LineEdit *snap_rotate;
LineEdit *snap_scale;
@@ -531,7 +532,9 @@ private:
void _instance_scene();
void _init_indicators();
+ void _init_grid();
void _finish_indicators();
+ void _finish_grid();
void _toggle_maximize_view(Object *p_viewport);
@@ -577,7 +580,7 @@ public:
ToolMode get_tool_mode() const { return tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
- bool is_snap_enabled() const { return snap_enabled; }
+ bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; }
float get_translate_snap() const { return snap_translate->get_text().to_double(); }
float get_rotate_snap() const { return snap_rotate->get_text().to_double(); }
float get_scale_snap() const { return snap_scale->get_text().to_double(); }
@@ -605,6 +608,7 @@ public:
UndoRedo *get_undo_redo() { return undo_redo; }
void add_control_to_menu_panel(Control *p_control);
+ void remove_control_from_menu_panel(Control *p_control);
VSplitContainer *get_shader_split();
HSplitContainer *get_palette_split();
diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp
new file mode 100644
index 0000000000..49816fe2ae
--- /dev/null
+++ b/editor/plugins/sprite_editor_plugin.cpp
@@ -0,0 +1,402 @@
+#include "sprite_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "scene/2d/mesh_instance_2d.h"
+#include "scene/gui/box_container.h"
+#include "thirdparty/misc/clipper.hpp"
+
+void SpriteEditor::_node_removed(Node *p_node) {
+
+ if (p_node == node) {
+ node = NULL;
+ options->hide();
+ }
+}
+
+void SpriteEditor::edit(Sprite *p_sprite) {
+
+ node = p_sprite;
+}
+
+#define PRECISION 10.0
+
+Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float epsilon = 2.0) {
+ int size = points.size();
+ ERR_FAIL_COND_V(size < 2, Vector<Vector2>());
+
+ ClipperLib::Path subj;
+ ClipperLib::PolyTree solution;
+ ClipperLib::PolyTree out;
+
+ for (int i = 0; i < points.size(); i++) {
+
+ subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION);
+ }
+ ClipperLib::ClipperOffset co;
+ co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
+ co.Execute(solution, epsilon * PRECISION);
+
+ ClipperLib::PolyNode *p = solution.GetFirst();
+
+ ERR_FAIL_COND_V(!p, points);
+
+ while (p->IsHole()) {
+ p = p->GetNext();
+ }
+
+ //turn the result into simply polygon (AKA, fix overlap)
+
+ //clamp into the specified rect
+ ClipperLib::Clipper cl;
+ cl.StrictlySimple(true);
+ cl.AddPath(p->Contour, ClipperLib::ptSubject, true);
+ //create the clipping rect
+ ClipperLib::Path clamp;
+ clamp.push_back(ClipperLib::IntPoint(0, 0));
+ clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0));
+ clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION));
+ clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION));
+ cl.AddPath(clamp, ClipperLib::ptClip, true);
+ cl.Execute(ClipperLib::ctIntersection, out);
+
+ Vector<Vector2> outPoints;
+ ClipperLib::PolyNode *p2 = out.GetFirst();
+ while (p2->IsHole()) {
+ p2 = p2->GetNext();
+ }
+
+ int lasti = p2->Contour.size() - 1;
+ Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION);
+ for (int i = 0; i < p2->Contour.size(); i++) {
+
+ Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION);
+ if (cur.distance_to(prev) > 0.5) {
+ outPoints.push_back(cur);
+ prev = cur;
+ }
+ }
+ return outPoints;
+}
+
+void SpriteEditor::_menu_option(int p_option) {
+
+ if (!node) {
+ return;
+ }
+
+ switch (p_option) {
+ case MENU_OPTION_CREATE_MESH_2D: {
+
+ _update_mesh_data();
+ debug_uv_dialog->popup_centered();
+ debug_uv->update();
+
+ } break;
+ }
+}
+
+void SpriteEditor::_update_mesh_data() {
+
+ Ref<Texture> texture = node->get_texture();
+ if (texture.is_null()) {
+ err_dialog->set_text(TTR("Sprite is empty!"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ if (node->get_hframes() > 1 || node->get_vframes() > 1) {
+ err_dialog->set_text(TTR("Can't convert a sprite using animation frames to mesh."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ Ref<Image> image = texture->get_data();
+ ERR_FAIL_COND(image.is_null());
+ Rect2 rect;
+ if (node->is_region())
+ rect = node->get_region_rect();
+ else
+ rect.size = Size2(image->get_width(), image->get_height());
+
+ Ref<BitMap> bm;
+ bm.instance();
+ bm->create_from_image_alpha(image);
+
+ int grow = island_merging->get_value();
+ if (grow > 0) {
+ bm->grow_mask(grow, rect);
+ }
+
+ float epsilon = simplification->get_value();
+
+ Vector<Vector<Vector2> > lines = bm->clip_opaque_to_polygons(rect, epsilon);
+
+ print_line("lines: " + itos(lines.size()));
+ uv_lines.clear();
+
+ computed_vertices.clear();
+ computed_uv.clear();
+ computed_indices.clear();
+
+ Size2 img_size = Vector2(image->get_width(), image->get_height());
+ for (int j = 0; j < lines.size(); j++) {
+ lines[j] = expand(lines[j], rect, epsilon);
+
+ int index_ofs = computed_vertices.size();
+
+ for (int i = 0; i < lines[j].size(); i++) {
+ Vector2 vtx = lines[j][i];
+ computed_uv.push_back(vtx / img_size);
+
+ vtx -= rect.position; //offset by rect position
+
+ //flip if flipped
+ if (node->is_flipped_h())
+ vtx.x = rect.size.x - vtx.x - 1.0;
+ if (node->is_flipped_v())
+ vtx.y = rect.size.y - vtx.y - 1.0;
+
+ if (node->is_centered())
+ vtx -= rect.size / 2.0;
+
+ computed_vertices.push_back(vtx);
+ }
+#if 0
+ Vector<Vector<Vector2> > polys = Geometry::decompose_polygon(lines[j]);
+ print_line("polygon: " + itos(polys.size()));
+
+ for (int i = 0; i < polys.size(); i++) {
+ for (int k = 0; k < polys[i].size(); k++) {
+
+ int idxn = (k + 1) % polys[i].size();
+ uv_lines.push_back(polys[i][k]);
+ uv_lines.push_back(polys[i][idxn]);
+ }
+ }
+#endif
+
+#if 1
+
+ Vector<int> poly = Geometry::triangulate_polygon(lines[j]);
+
+ for (int i = 0; i < poly.size(); i += 3) {
+ for (int k = 0; k < 3; k++) {
+ int idx = i + k;
+ int idxn = i + (k + 1) % 3;
+ uv_lines.push_back(lines[j][poly[idx]]);
+ uv_lines.push_back(lines[j][poly[idxn]]);
+
+ computed_indices.push_back(poly[idx] + index_ofs);
+ }
+ }
+#endif
+
+#if 0
+ for (int i = 0; i < lines[j].size() - 1; i++) {
+ uv_lines.push_back(lines[j][i]);
+ uv_lines.push_back(lines[j][i + 1]);
+ }
+#endif
+ }
+
+ debug_uv->update();
+}
+
+void SpriteEditor::_create_mesh_node() {
+
+ if (computed_vertices.size() < 3) {
+ err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ Ref<ArrayMesh> mesh;
+ mesh.instance();
+
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ a[Mesh::ARRAY_VERTEX] = computed_vertices;
+ a[Mesh::ARRAY_TEX_UV] = computed_uv;
+ a[Mesh::ARRAY_INDEX] = computed_indices;
+
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
+
+ MeshInstance2D *mesh_instance = memnew(MeshInstance2D);
+ mesh_instance->set_mesh(mesh);
+ EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance);
+}
+
+#if 0
+void SpriteEditor::_create_uv_lines() {
+
+ Ref<Mesh> sprite = node->get_sprite();
+ ERR_FAIL_COND(!sprite.is_valid());
+
+ Set<SpriteEditorEdgeSort> edges;
+ uv_lines.clear();
+ for (int i = 0; i < sprite->get_surface_count(); i++) {
+ if (sprite->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
+ continue;
+ Array a = sprite->surface_get_arrays(i);
+
+ PoolVector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2];
+ if (uv.size() == 0) {
+ err_dialog->set_text(TTR("Model has no UV in this layer"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ PoolVector<Vector2>::Read r = uv.read();
+
+ PoolVector<int> indices = a[Mesh::ARRAY_INDEX];
+ PoolVector<int>::Read ri;
+
+ int ic;
+ bool use_indices;
+
+ if (indices.size()) {
+ ic = indices.size();
+ ri = indices.read();
+ use_indices = true;
+ } else {
+ ic = uv.size();
+ use_indices = false;
+ }
+
+ for (int j = 0; j < ic; j += 3) {
+
+ for (int k = 0; k < 3; k++) {
+
+ SpriteEditorEdgeSort edge;
+ if (use_indices) {
+ edge.a = r[ri[j + k]];
+ edge.b = r[ri[j + ((k + 1) % 3)]];
+ } else {
+ edge.a = r[j + k];
+ edge.b = r[j + ((k + 1) % 3)];
+ }
+
+ if (edges.has(edge))
+ continue;
+
+ uv_lines.push_back(edge.a);
+ uv_lines.push_back(edge.b);
+ edges.insert(edge);
+ }
+ }
+ }
+
+ debug_uv_dialog->popup_centered_minsize();
+}
+#endif
+void SpriteEditor::_debug_uv_draw() {
+
+ if (uv_lines.size() == 0)
+ return;
+
+ Ref<Texture> tex = node->get_texture();
+ ERR_FAIL_COND(!tex.is_valid());
+ debug_uv->set_clip_contents(true);
+ debug_uv->draw_texture(tex, Point2());
+ debug_uv->set_custom_minimum_size(tex->get_size());
+ //debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
+ debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7));
+}
+
+void SpriteEditor::_bind_methods() {
+
+ ClassDB::bind_method("_menu_option", &SpriteEditor::_menu_option);
+ ClassDB::bind_method("_debug_uv_draw", &SpriteEditor::_debug_uv_draw);
+ ClassDB::bind_method("_update_mesh_data", &SpriteEditor::_update_mesh_data);
+ ClassDB::bind_method("_create_mesh_node", &SpriteEditor::_create_mesh_node);
+}
+
+SpriteEditor::SpriteEditor() {
+
+ options = memnew(MenuButton);
+
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->set_text(TTR("Sprite"));
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Sprite", "EditorIcons"));
+
+ options->get_popup()->add_item(TTR("Convert to 2D Mesh"), MENU_OPTION_CREATE_MESH_2D);
+
+ options->get_popup()->connect("id_pressed", this, "_menu_option");
+
+ err_dialog = memnew(AcceptDialog);
+ add_child(err_dialog);
+
+ debug_uv_dialog = memnew(ConfirmationDialog);
+ debug_uv_dialog->get_ok()->set_text(TTR("Create 2D Mesh"));
+ debug_uv_dialog->set_title("Mesh 2D Preview");
+ VBoxContainer *vb = memnew(VBoxContainer);
+ debug_uv_dialog->add_child(vb);
+ ScrollContainer *scroll = memnew(ScrollContainer);
+ scroll->set_custom_minimum_size(Size2(800, 500) * EDSCALE);
+ scroll->set_enable_h_scroll(true);
+ scroll->set_enable_v_scroll(true);
+ vb->add_margin_child(TTR("Preview:"), scroll, true);
+ debug_uv = memnew(Control);
+ debug_uv->connect("draw", this, "_debug_uv_draw");
+ scroll->add_child(debug_uv);
+ debug_uv_dialog->connect("confirmed", this, "_create_mesh_node");
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->add_child(memnew(Label(TTR("Simplification: "))));
+ simplification = memnew(SpinBox);
+ simplification->set_min(0.01);
+ simplification->set_max(10.00);
+ simplification->set_step(0.01);
+ simplification->set_value(2);
+ hb->add_child(simplification);
+ hb->add_spacer();
+ hb->add_child(memnew(Label(TTR("Grow (Pixels): "))));
+ island_merging = memnew(SpinBox);
+ island_merging->set_min(0);
+ island_merging->set_max(10);
+ island_merging->set_step(1);
+ island_merging->set_value(2);
+ hb->add_child(island_merging);
+ hb->add_spacer();
+ update_preview = memnew(Button);
+ update_preview->set_text(TTR("Update Preview"));
+ update_preview->connect("pressed", this, "_update_mesh_data");
+ hb->add_child(update_preview);
+ vb->add_margin_child(TTR("Settings:"), hb);
+
+ add_child(debug_uv_dialog);
+}
+
+void SpriteEditorPlugin::edit(Object *p_object) {
+
+ sprite_editor->edit(Object::cast_to<Sprite>(p_object));
+}
+
+bool SpriteEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("Sprite");
+}
+
+void SpriteEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ sprite_editor->options->show();
+ } else {
+
+ sprite_editor->options->hide();
+ sprite_editor->edit(NULL);
+ }
+}
+
+SpriteEditorPlugin::SpriteEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ sprite_editor = memnew(SpriteEditor);
+ editor->get_viewport()->add_child(sprite_editor);
+ make_visible(false);
+
+ //sprite_editor->options->hide();
+}
+
+SpriteEditorPlugin::~SpriteEditorPlugin() {
+}
diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h
new file mode 100644
index 0000000000..17aa3eb1f9
--- /dev/null
+++ b/editor/plugins/sprite_editor_plugin.h
@@ -0,0 +1,73 @@
+#ifndef SPRITE_EDITOR_PLUGIN_H
+#define SPRITE_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/2d/sprite.h"
+#include "scene/gui/spin_box.h"
+
+class SpriteEditor : public Control {
+
+ GDCLASS(SpriteEditor, Control);
+
+ enum Menu {
+ MENU_OPTION_CREATE_MESH_2D,
+ };
+
+ Sprite *node;
+
+ MenuButton *options;
+
+ ConfirmationDialog *outline_dialog;
+
+ AcceptDialog *err_dialog;
+
+ ConfirmationDialog *debug_uv_dialog;
+ Control *debug_uv;
+ Vector<Vector2> uv_lines;
+
+ Vector<Vector2> computed_vertices;
+ Vector<Vector2> computed_uv;
+ Vector<int> computed_indices;
+
+ SpinBox *simplification;
+ SpinBox *island_merging;
+ Button *update_preview;
+
+ void _menu_option(int p_option);
+
+ //void _create_uv_lines();
+ friend class SpriteEditorPlugin;
+
+ void _debug_uv_draw();
+ void _update_mesh_data();
+ void _create_mesh_node();
+
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+public:
+ void edit(Sprite *p_sprite);
+ SpriteEditor();
+};
+
+class SpriteEditorPlugin : public EditorPlugin {
+
+ GDCLASS(SpriteEditorPlugin, EditorPlugin);
+
+ SpriteEditor *sprite_editor;
+ EditorNode *editor;
+
+public:
+ virtual String get_name() const { return "Sprite"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ SpriteEditorPlugin(EditorNode *p_node);
+ ~SpriteEditorPlugin();
+};
+
+#endif // SPRITE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 7a4eee0344..a9afc7a670 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -45,6 +45,12 @@ void SpriteFramesEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
load->set_icon(get_icon("Load", "EditorIcons"));
+ copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
+ paste->set_icon(get_icon("ActionPaste", "EditorIcons"));
+ empty->set_icon(get_icon("InsertBefore", "EditorIcons"));
+ empty2->set_icon(get_icon("InsertAfter", "EditorIcons"));
+ move_up->set_icon(get_icon("MoveLeft", "EditorIcons"));
+ move_down->set_icon(get_icon("MoveRight", "EditorIcons"));
_delete->set_icon(get_icon("Remove", "EditorIcons"));
new_anim->set_icon(get_icon("New", "EditorIcons"));
remove_anim->set_icon(get_icon("Remove", "EditorIcons"));
@@ -398,8 +404,6 @@ void SpriteFramesEditor::_animation_add() {
animations->grab_focus();
}
void SpriteFramesEditor::_animation_remove() {
-
- //fuck everything
if (updating)
return;
@@ -460,8 +464,6 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) {
List<StringName> anim_names;
- anim_names.sort_custom<StringName::AlphCompare>();
-
frames->get_animation_list(&anim_names);
anim_names.sort_custom<StringName::AlphCompare>();
@@ -740,27 +742,35 @@ SpriteFramesEditor::SpriteFramesEditor() {
hbc->add_child(load);
copy = memnew(Button);
- copy->set_text(TTR("Copy"));
+ copy->set_flat(true);
+ copy->set_tooltip(TTR("Copy"));
hbc->add_child(copy);
paste = memnew(Button);
- paste->set_text(TTR("Paste"));
+ paste->set_flat(true);
+ paste->set_tooltip(TTR("Paste"));
hbc->add_child(paste);
empty = memnew(Button);
- empty->set_text(TTR("Insert Empty (Before)"));
+ empty->set_flat(true);
+ empty->set_tooltip(TTR("Insert Empty (Before)"));
hbc->add_child(empty);
empty2 = memnew(Button);
- empty2->set_text(TTR("Insert Empty (After)"));
+ empty2->set_flat(true);
+ empty2->set_tooltip(TTR("Insert Empty (After)"));
hbc->add_child(empty2);
+ hbc->add_spacer(false);
+
move_up = memnew(Button);
- move_up->set_text(TTR("Move (Before)"));
+ move_up->set_flat(true);
+ move_up->set_tooltip(TTR("Move (Before)"));
hbc->add_child(move_up);
move_down = memnew(Button);
- move_down->set_text(TTR("Move (After)"));
+ move_down->set_flat(true);
+ move_down->set_tooltip(TTR("Move (After)"));
hbc->add_child(move_down);
_delete = memnew(Button);
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 36a578037e..e891850870 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -75,6 +75,9 @@ void TextureEditor::_notification(int p_what) {
// In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient
ofs_y = 0;
tex_height = size.height;
+ } else if (Object::cast_to<GradientTexture>(*texture)) {
+ ofs_y = size.height / 4.0;
+ tex_height = size.height / 2.0;
}
draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height));
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index e04798d6d6..5ba3931689 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -57,6 +57,9 @@ void TextureRegionEditor::_region_draw() {
base_tex = obj_styleBox->get_texture();
else if (atlas_tex.is_valid())
base_tex = atlas_tex->get_atlas();
+ else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile))
+ base_tex = tile_set->tile_get_texture(selected_tile);
+
if (base_tex.is_null())
return;
@@ -281,6 +284,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
r = obj_styleBox->get_region_rect();
else if (atlas_tex.is_valid())
r = atlas_tex->get_region();
+ else if (tile_set.is_valid() && selected_tile != -1)
+ r = tile_set->tile_get_region(selected_tile);
rect.expand_to(r.position);
rect.expand_to(r.position + r.size);
}
@@ -297,6 +302,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
} else if (atlas_tex.is_valid()) {
undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect);
undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
+ } else if (tile_set.is_valid() && selected_tile != -1) {
+ undo_redo->add_do_method(tile_set.ptr(), "tile_set_region", selected_tile, rect);
+ undo_redo->add_undo_method(tile_set.ptr(), "tile_set_region", selected_tile, tile_set->tile_get_region(selected_tile));
}
undo_redo->add_do_method(edit_draw, "update");
undo_redo->add_undo_method(edit_draw, "update");
@@ -319,6 +327,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
rect_prev = obj_styleBox->get_region_rect();
else if (atlas_tex.is_valid())
rect_prev = atlas_tex->get_region();
+ else if (tile_set.is_valid() && selected_tile != -1)
+ rect_prev = tile_set->tile_get_region(selected_tile);
for (int i = 0; i < 8; i++) {
Vector2 tuv = endpoints[i];
@@ -362,6 +372,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
} else if (obj_styleBox.is_valid()) {
undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect());
undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", rect_prev);
+ } else if (tile_set.is_valid()) {
+ undo_redo->add_do_method(tile_set.ptr(), "tile_set_region", selected_tile, tile_set->tile_get_region(selected_tile));
+ undo_redo->add_undo_method(tile_set.ptr(), "tile_set_region", selected_tile, rect_prev);
}
drag_index = -1;
}
@@ -582,10 +595,13 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) {
obj_styleBox->set_region_rect(rect);
else if (atlas_tex.is_valid())
atlas_tex->set_region(rect);
+ else if (tile_set.is_valid() && selected_tile != -1)
+ tile_set->tile_set_region(selected_tile, rect);
}
void TextureRegionEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_READY: {
zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons"));
zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons"));
@@ -596,11 +612,12 @@ void TextureRegionEditor::_notification(int p_what) {
}
void TextureRegionEditor::_node_removed(Object *p_obj) {
- if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) {
+ if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr() || p_obj == tile_set.ptr()) {
node_ninepatch = NULL;
node_sprite = NULL;
obj_styleBox = Ref<StyleBox>(NULL);
atlas_tex = Ref<AtlasTexture>(NULL);
+ tile_set = Ref<TileSet>(NULL);
hide();
}
}
@@ -632,6 +649,8 @@ void TextureRegionEditor::edit(Object *p_obj) {
obj_styleBox->remove_change_receptor(this);
if (atlas_tex.is_valid())
atlas_tex->remove_change_receptor(this);
+ if (tile_set.is_valid())
+ tile_set->remove_change_receptor(this);
if (p_obj) {
node_sprite = Object::cast_to<Sprite>(p_obj);
node_ninepatch = Object::cast_to<NinePatchRect>(p_obj);
@@ -639,6 +658,8 @@ void TextureRegionEditor::edit(Object *p_obj) {
obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj));
if (Object::cast_to<AtlasTexture>(p_obj))
atlas_tex = Ref<AtlasTexture>(Object::cast_to<AtlasTexture>(p_obj));
+ if (Object::cast_to<TileSet>(p_obj))
+ tile_set = Ref<TileSet>(Object::cast_to<TileSet>(p_obj));
p_obj->add_change_receptor(this);
_edit_region();
} else {
@@ -646,6 +667,7 @@ void TextureRegionEditor::edit(Object *p_obj) {
node_ninepatch = NULL;
obj_styleBox = Ref<StyleBoxTexture>(NULL);
atlas_tex = Ref<AtlasTexture>(NULL);
+ tile_set = Ref<TileSet>(NULL);
}
edit_draw->update();
}
@@ -668,8 +690,11 @@ void TextureRegionEditor::_edit_region() {
texture = obj_styleBox->get_texture();
else if (atlas_tex.is_valid())
texture = atlas_tex->get_atlas();
+ else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile))
+ texture = tile_set->tile_get_texture(selected_tile);
if (texture.is_null()) {
+ edit_draw->update();
return;
}
@@ -735,6 +760,8 @@ void TextureRegionEditor::_edit_region() {
rect = obj_styleBox->get_region_rect();
else if (atlas_tex.is_valid())
rect = atlas_tex->get_region();
+ else if (tile_set.is_valid() && selected_tile != -1)
+ rect = tile_set->tile_get_region(selected_tile);
edit_draw->update();
}
@@ -753,11 +780,14 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
node_ninepatch = NULL;
obj_styleBox = Ref<StyleBoxTexture>(NULL);
atlas_tex = Ref<AtlasTexture>(NULL);
+ tile_set = Ref<TileSet>(NULL);
editor = p_editor;
undo_redo = editor->get_undo_redo();
+ selected_tile = -1;
snap_step = Vector2(10, 10);
snap_separation = Vector2(0, 0);
+ snap_mode = SNAP_NONE;
edited_margin = -1;
drag_index = -1;
drag = false;
@@ -775,12 +805,10 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
snap_mode_button->set_text(TTR("<None>"));
PopupMenu *p = snap_mode_button->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_item(TTR("<None>"), 0);
- p->add_item(TTR("Pixel Snap"), 1);
- p->add_item(TTR("Grid Snap"), 2);
- p->add_item(TTR("Auto Slice"), 3);
- for (int i = 0; i < 4; i++)
- p->set_item_as_checkable(i, true);
+ p->add_radio_check_item(TTR("<None>"), 0);
+ p->add_radio_check_item(TTR("Pixel Snap"), 1);
+ p->add_radio_check_item(TTR("Grid Snap"), 2);
+ p->add_radio_check_item(TTR("Auto Slice"), 3);
p->set_item_checked(0, true);
p->connect("id_pressed", this, "_set_snap_mode");
hb_grid = memnew(HBoxContainer);
@@ -903,11 +931,11 @@ bool TextureRegionEditorPlugin::handles(Object *p_object) const {
void TextureRegionEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- region_button->show();
- if (region_button->is_pressed())
+ texture_region_button->show();
+ if (texture_region_button->is_pressed())
region_editor->show();
} else {
- region_button->hide();
+ texture_region_button->hide();
region_editor->edit(NULL);
region_editor->hide();
}
@@ -961,10 +989,10 @@ TextureRegionEditorPlugin::TextureRegionEditorPlugin(EditorNode *p_node) {
editor = p_node;
region_editor = memnew(TextureRegionEditor(p_node));
- region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor);
- region_button->set_tooltip(TTR("Texture Region Editor"));
+ texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), region_editor);
+ texture_region_button->set_tooltip(TTR("Texture Region Editor"));
region_editor->set_custom_minimum_size(Size2(0, 200));
region_editor->hide();
- region_button->hide();
+ texture_region_button->hide();
}
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index cf9396bd5b..1244953a3f 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -38,6 +38,7 @@
#include "scene/gui/nine_patch_rect.h"
#include "scene/resources/style_box.h"
#include "scene/resources/texture.h"
+#include "scene/resources/tile_set.h"
/**
@author Mariano Suligoy
@@ -55,6 +56,8 @@ class TextureRegionEditor : public Control {
};
friend class TextureRegionEditorPlugin;
+ friend class TileSetEditor;
+ friend class TileSetEditorPlugin;
MenuButton *snap_mode_button;
TextureRect *icon_zoom;
ToolButton *zoom_in;
@@ -88,12 +91,14 @@ class TextureRegionEditor : public Control {
Sprite *node_sprite;
Ref<StyleBoxTexture> obj_styleBox;
Ref<AtlasTexture> atlas_tex;
+ Ref<TileSet> tile_set;
Rect2 rect;
Rect2 rect_prev;
float prev_margin;
int edited_margin;
List<Rect2> autoslice_cache;
+ int selected_tile;
bool drag;
bool creating;
@@ -134,7 +139,7 @@ public:
class TextureRegionEditorPlugin : public EditorPlugin {
GDCLASS(TextureRegionEditorPlugin, EditorPlugin);
- Button *region_button;
+ Button *texture_region_button;
TextureRegionEditor *region_editor;
EditorNode *editor;
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index f51e691be3..92b95963f9 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -244,7 +244,7 @@ void ThemeEditor::_save_template_cbk(String fname) {
file->store_line("; ");
file->store_line("; ******************* ");
file->store_line("; ");
- file->store_line("; Template Generated Using: " + String(VERSION_MKSTRING));
+ file->store_line("; Template Generated Using: " + String(VERSION_FULL_BUILD));
file->store_line("; ");
file->store_line("; ");
file->store_line("");
@@ -632,7 +632,7 @@ ThemeEditor::ThemeEditor() {
main_vb->add_child(hb_menu);
theme_menu = memnew(MenuButton);
- theme_menu->set_text(TTR("Edit theme.."));
+ theme_menu->set_text(TTR("Edit theme..."));
theme_menu->set_flat(false);
theme_menu->set_tooltip(TTR("Theme editing menu."));
theme_menu->get_popup()->add_item(TTR("Add Item"), POPUP_ADD);
@@ -691,7 +691,11 @@ ThemeEditor::ThemeEditor() {
test_menu_button->get_popup()->add_separator();
test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
- test_menu_button->get_popup()->set_item_checked(2, true);
+ test_menu_button->get_popup()->set_item_checked(3, true);
+ test_menu_button->get_popup()->add_separator();
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));
+ test_menu_button->get_popup()->set_item_checked(6, true);
first_vb->add_child(test_menu_button);
OptionButton *test_option_button = memnew(OptionButton);
@@ -740,7 +744,7 @@ ThemeEditor::ThemeEditor() {
item = test_tree->create_item(test_tree->get_root());
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_editable(0, true);
- item->set_text(0, "check");
+ item->set_text(0, "Check");
item = test_tree->create_item(test_tree->get_root());
item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
item->set_editable(0, true);
@@ -749,7 +753,7 @@ ThemeEditor::ThemeEditor() {
item = test_tree->create_item(test_tree->get_root());
item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
item->set_editable(0, true);
- item->set_text(0, TTR("Have,Many,Several,Options!"));
+ item->set_text(0, TTR("Has,Many,Options"));
item->set_range(0, 2);
VBoxContainer *third_vb = memnew(VBoxContainer);
@@ -780,58 +784,6 @@ ThemeEditor::ThemeEditor() {
main_hb->add_constant_override("separation", 20 * EDSCALE);
- /*
- test_h_scroll = memnew( HScrollBar );
- test_h_scroll->set_position( Point2( 25, 225 ) );
- test_h_scroll->set_size( Point2( 150, 5 ) );
- panel->add_child(test_h_scroll);
-
- line_edit = memnew( LineEdit );
- line_edit->set_position( Point2( 25, 275 ) );
- line_edit->set_size( Point2( 150, 5 ) );
- line_edit->set_text("Line Edit");
- panel->add_child(line_edit);
-
- test_v_scroll = memnew( VScrollBar );
- test_v_scroll->set_position( Point2( 200, 25 ) );
- test_v_scroll->set_size( Point2( 5, 150 ) );
- panel->add_child(test_v_scroll);
-
- test_tree = memnew(Tree);
- test_tree->set_position( Point2( 300, 25 ) );
- test_tree->set_size( Point2( 200, 200 ) );
- panel->add_child(test_tree);
-
-
- TreeItem *item = test_tree->create_item();
- item->set_editable(0,true);
- item->set_text(0,"root");
- item = test_tree->create_item( test_tree->get_root() );
- item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- item->set_editable(0,true);
- item->set_text(0,"check");
- item = test_tree->create_item( test_tree->get_root() );
- item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
- item->set_editable(0,true);
- item->set_range_config(0,0,20,0.1);
- item->set_range(0,2);
- item = test_tree->create_item( test_tree->get_root() );
- item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
- item->set_editable(0,true);
- item->set_text(0,"Have,Many,Several,Options!"));
- item->set_range(0,2);
-
- Button *fd_button= memnew( Button );
- fd_button->set_position(Point2(300,275));
- fd_button->set_text("Open File Dialog");
- panel->add_child(fd_button);
-
- test_file_dialog = memnew( EditorFileDialog );
- panel->add_child(test_file_dialog);
-
- fd_button->connect("pressed", this,"_open_file_dialog");
-*/
-
add_del_dialog = memnew(ConfirmationDialog);
add_del_dialog->hide();
add_child(add_del_dialog);
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index a102d99d1c..72b3af5a09 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -48,6 +48,20 @@ void TileMapEditor::_notification(int p_what) {
} break;
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+
+ bool new_show_tile_info = EditorSettings::get_singleton()->get("editors/tile_map/show_tile_info_on_hover");
+ if (new_show_tile_info != show_tile_info) {
+ show_tile_info = new_show_tile_info;
+ tile_info->set_visible(show_tile_info);
+ }
+
+ if (is_visible_in_tree()) {
+ _update_palette();
+ }
+
+ } // fallthrough
+
case NOTIFICATION_ENTER_TREE: {
transp->set_icon(get_icon("Transpose", "EditorIcons"));
@@ -60,19 +74,14 @@ void TileMapEditor::_notification(int p_what) {
search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
- } break;
-
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
-
- bool new_show_tile_info = EditorSettings::get_singleton()->get("editors/tile_map/show_tile_info_on_hover");
- if (new_show_tile_info != show_tile_info) {
- show_tile_info = new_show_tile_info;
- tile_info->set_visible(show_tile_info);
- }
+ PopupMenu *p = options->get_popup();
+ p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ToolSelect", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_MOVE), get_icon("ToolMove", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_DUPLICATE), get_icon("Duplicate", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons"));
- if (is_visible_in_tree()) {
- _update_palette();
- }
} break;
}
}
@@ -139,6 +148,23 @@ void TileMapEditor::_menu_option(int p_option) {
canvas_item_editor->update();
} break;
+ case OPTION_FIX_INVALID: {
+
+ undo_redo->create_action(TTR("Fix Invalid Tiles"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ node->fix_invalid_tiles();
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
+ undo_redo->commit_action();
+
+ } break;
+ case OPTION_MOVE: {
+
+ if (selection_active) {
+ _update_copydata();
+ tool = TOOL_MOVING;
+ canvas_item_editor->update();
+ }
+ } break;
}
}
@@ -303,7 +329,7 @@ void TileMapEditor::_update_palette() {
if (tex.is_valid()) {
Rect2 region = tileset->tile_get_region(entries[i].id);
- if (tileset->tile_get_is_autotile(entries[i].id)) {
+ if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE) {
int spacing = tileset->autotile_get_spacing(entries[i].id);
region.size = tileset->autotile_get_size(entries[i].id);
region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id);
@@ -506,7 +532,7 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h
Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell);
Rect2 r = node->get_tileset()->tile_get_region(p_cell);
- if (node->get_tileset()->tile_get_is_autotile(p_cell)) {
+ if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE) {
int spacing = node->get_tileset()->autotile_get_spacing(p_cell);
r.size = node->get_tileset()->autotile_get_size(p_cell);
r.position += (r.size + Vector2(spacing, spacing)) * node->get_tileset()->autotile_get_icon_coordinate(p_cell);
@@ -698,7 +724,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (mb->get_shift()) {
+#ifdef APPLE_STYLE_KEYS
+ if (mb->get_command())
+#else
if (mb->get_control())
+#endif
tool = TOOL_RECTANGLE_PAINT;
else
tool = TOOL_LINE_PAINT;
@@ -708,9 +738,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
-
+#ifdef APPLE_STYLE_KEYS
+ if (mb->get_command()) {
+#else
if (mb->get_control()) {
-
+#endif
tool = TOOL_PICKING;
_pick_tile(over_tile);
@@ -812,6 +844,29 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
copydata.clear();
canvas_item_editor->update();
+ } else if (tool == TOOL_MOVING) {
+
+ Point2 ofs = over_tile - rectangle.position;
+
+ undo_redo->create_action(TTR("Move"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
+ for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
+
+ _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ }
+ }
+ for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
+
+ _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ }
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
+ undo_redo->commit_action();
+
+ copydata.clear();
+ selection_active = false;
+
+ canvas_item_editor->update();
} else if (tool == TOOL_SELECTING) {
@@ -871,6 +926,16 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
+ if (tool == TOOL_MOVING) {
+
+ tool = TOOL_NONE;
+ copydata.clear();
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+
if (tool == TOOL_NONE) {
paint_undo.clear();
@@ -881,8 +946,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
if (mb->get_shift()) {
-
+#ifdef APPLE_STYLE_KEYS
+ if (mb->get_command())
+#else
if (mb->get_control())
+#endif
tool = TOOL_RECTANGLE_ERASE;
else
tool = TOOL_LINE_ERASE;
@@ -1078,7 +1146,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (k->get_scancode() == KEY_ESCAPE) {
- if (tool == TOOL_DUPLICATING)
+ if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING)
copydata.clear();
else if (tool == TOOL_SELECTING || selection_active)
selection_active = false;
@@ -1133,6 +1201,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
}
+ if (ED_IS_SHORTCUT("tile_map_editor/move_selection", p_event)) {
+ if (selection_active) {
+ _update_copydata();
+ tool = TOOL_MOVING;
+ canvas_item_editor->update();
+ return true;
+ }
+ }
if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) {
search_box->select_all();
search_box->grab_focus();
@@ -1142,18 +1218,21 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) {
flip_h = !flip_h;
mirror_x->set_pressed(flip_h);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) {
flip_v = !flip_v;
mirror_y->set_pressed(flip_v);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
transpose = !transpose;
transp->set_pressed(transpose);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
@@ -1326,7 +1405,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
_draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
}
}
- } else if (tool == TOOL_DUPLICATING) {
+ } else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) {
if (copydata.empty())
return;
@@ -1511,8 +1590,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
bucket_cache_tile = -1;
bucket_cache_visited = 0;
- ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase selection"), KEY_DELETE);
- ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find tile"), KEY_MASK_CMD + KEY_F);
+ ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE);
+ ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F);
ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"), KEY_T);
ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A);
ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S);
@@ -1573,8 +1652,11 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT);
+ p->add_shortcut(ED_SHORTCUT("tile_map_editor/move_selection", TTR("Move Selection"), KEY_MASK_CMD + KEY_M), OPTION_MOVE);
p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE);
p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION);
+ p->add_separator();
+ p->add_item(TTR("Fix Invalid Tiles"), OPTION_FIX_INVALID);
p->connect("id_pressed", this, "_menu_option");
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 0a937e200e..642870aec0 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -61,6 +61,7 @@ class TileMapEditor : public VBoxContainer {
TOOL_BUCKET,
TOOL_PICKING,
TOOL_DUPLICATING,
+ TOOL_MOVING
};
enum Options {
@@ -71,6 +72,8 @@ class TileMapEditor : public VBoxContainer {
OPTION_DUPLICATE,
OPTION_ERASE_SELECTION,
OPTION_PAINTING,
+ OPTION_FIX_INVALID,
+ OPTION_MOVE
};
TileMap *node;
@@ -122,12 +125,11 @@ class TileMapEditor : public VBoxContainer {
bool yf;
bool tr;
- CellOp() {
- idx = -1;
- xf = false;
- yf = false;
- tr = false;
- }
+ CellOp() :
+ idx(TileMap::INVALID_CELL),
+ xf(false),
+ yf(false),
+ tr(false) {}
};
Map<Point2i, CellOp> paint_undo;
@@ -138,8 +140,12 @@ class TileMapEditor : public VBoxContainer {
bool flip_h;
bool flip_v;
bool transpose;
- int auto_x;
- int auto_y;
+
+ TileData() :
+ cell(TileMap::INVALID_CELL),
+ flip_h(false),
+ flip_v(false),
+ transpose(false) {}
};
List<TileData> copydata;
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 1dedf92452..385fa24ad8 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -37,6 +37,9 @@
void TileSetEditor::edit(const Ref<TileSet> &p_tileset) {
tileset = p_tileset;
+ tileset->add_change_receptor(this);
+
+ update_tile_list();
}
void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
@@ -146,6 +149,7 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
p_library->tile_set_light_occluder(id, occluder);
p_library->tile_set_occluder_offset(id, -phys_offset);
p_library->tile_set_navigation_polygon_offset(id, -phys_offset);
+ p_library->tile_set_z_index(id, mi->get_z_index());
}
}
@@ -188,6 +192,7 @@ void TileSetEditor::_name_dialog_confirm(const String &name) {
if (tileset->has_tile(id)) {
tileset->remove_tile(id);
+ update_tile_list();
} else {
err_dialog->set_text(TTR("Could not find tile:") + " " + name);
err_dialog->popup_centered(Size2(300, 60));
@@ -202,8 +207,9 @@ void TileSetEditor::_menu_cbk(int p_option) {
switch (p_option) {
case MENU_OPTION_ADD_ITEM: {
-
tileset->create_tile(tileset->get_last_unused_tile_id());
+ tileset->tile_set_name(tileset->get_last_unused_tile_id() - 1, itos(tileset->get_last_unused_tile_id() - 1));
+ update_tile_list();
} break;
case MENU_OPTION_REMOVE_ITEM: {
@@ -235,106 +241,77 @@ void TileSetEditor::_bind_methods() {
ClassDB::bind_method("_menu_cbk", &TileSetEditor::_menu_cbk);
ClassDB::bind_method("_menu_confirm", &TileSetEditor::_menu_confirm);
ClassDB::bind_method("_name_dialog_confirm", &TileSetEditor::_name_dialog_confirm);
+ ClassDB::bind_method("_on_tile_list_selected", &TileSetEditor::_on_tile_list_selected);
+ ClassDB::bind_method("_on_edit_mode_changed", &TileSetEditor::_on_edit_mode_changed);
+ ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw);
+ ClassDB::bind_method("_on_workspace_draw", &TileSetEditor::_on_workspace_draw);
+ ClassDB::bind_method("_on_workspace_input", &TileSetEditor::_on_workspace_input);
+ ClassDB::bind_method("_on_tool_clicked", &TileSetEditor::_on_tool_clicked);
+ ClassDB::bind_method("_on_priority_changed", &TileSetEditor::_on_priority_changed);
+ ClassDB::bind_method("_on_grid_snap_toggled", &TileSetEditor::_on_grid_snap_toggled);
+ ClassDB::bind_method("_set_snap_step_x", &TileSetEditor::_set_snap_step_x);
+ ClassDB::bind_method("_set_snap_step_y", &TileSetEditor::_set_snap_step_y);
+ ClassDB::bind_method("_set_snap_off_x", &TileSetEditor::_set_snap_off_x);
+ ClassDB::bind_method("_set_snap_off_y", &TileSetEditor::_set_snap_off_y);
+ ClassDB::bind_method("_set_snap_sep_x", &TileSetEditor::_set_snap_sep_x);
+ ClassDB::bind_method("_set_snap_sep_y", &TileSetEditor::_set_snap_sep_y);
}
-TileSetEditor::TileSetEditor(EditorNode *p_editor) {
-
- menu = memnew(MenuButton);
- CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu);
- menu->hide();
- menu->set_text(TTR("Tile Set"));
- menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
- menu->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM);
- menu->get_popup()->add_separator();
- menu->get_popup()->add_item(TTR("Create from Scene"), MENU_OPTION_CREATE_FROM_SCENE);
- menu->get_popup()->add_item(TTR("Merge from Scene"), MENU_OPTION_MERGE_FROM_SCENE);
- menu->get_popup()->connect("id_pressed", this, "_menu_cbk");
- editor = p_editor;
- cd = memnew(ConfirmationDialog);
- add_child(cd);
- cd->get_ok()->connect("pressed", this, "_menu_confirm");
-
- nd = memnew(EditorNameDialog);
- add_child(nd);
- nd->set_hide_on_ok(true);
- nd->get_line_edit()->set_margin(MARGIN_TOP, 28);
- nd->connect("name_confirmed", this, "_name_dialog_confirm");
-
- err_dialog = memnew(AcceptDialog);
- add_child(err_dialog);
- err_dialog->set_title(TTR("Error"));
-}
-
-void TileSetEditorPlugin::edit(Object *p_node) {
-
- if (Object::cast_to<TileSet>(p_node)) {
- tileset_editor->edit(Object::cast_to<TileSet>(p_node));
- tileset_editor->show();
- autotile_editor->edit(p_node);
- } else
- tileset_editor->hide();
-}
-
-bool TileSetEditorPlugin::handles(Object *p_node) const {
-
- return p_node->is_class("TileSet");
-}
-
-void TileSetEditorPlugin::make_visible(bool p_visible) {
-
- if (p_visible) {
- tileset_editor->show();
- tileset_editor->menu->show();
- autotile_button->show();
- autotile_editor->side_panel->show();
- if (autotile_button->is_pressed()) {
- autotile_editor->show();
- }
- } else {
- tileset_editor->hide();
- tileset_editor->menu->hide();
- autotile_editor->side_panel->hide();
- autotile_editor->hide();
- autotile_button->hide();
+void TileSetEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
+ tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons"));
+ tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons"));
+ tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons"));
+ tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons"));
+ tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons"));
+ tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons"));
+ tools[SHAPE_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons"));
+ tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons"));
+ tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons"));
+ tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons"));
}
}
-TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) {
-
- tileset_editor = memnew(TileSetEditor(p_node));
-
- add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, tileset_editor);
- tileset_editor->set_anchors_and_margins_preset(Control::PRESET_TOP_WIDE);
- tileset_editor->set_end(Point2(0, 22));
- tileset_editor->hide();
-
- autotile_editor = memnew(AutotileEditor(p_node));
- add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, autotile_editor->side_panel);
- autotile_editor->side_panel->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- autotile_editor->side_panel->set_custom_minimum_size(Size2(200, 0));
- autotile_editor->side_panel->hide();
- autotile_button = p_node->add_bottom_panel_item(TTR("Autotiles"), autotile_editor);
- autotile_button->hide();
+void TileSetEditor::_changed_callback(Object *p_changed, const char *p_prop) {
+ if (p_prop == StringName("region")) {
+ update_tile_list_icon();
+ preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
+ } else if (p_prop == StringName("name")) {
+ update_tile_list_icon();
+ } else if (p_prop == StringName("texture") || p_prop == StringName("modulate") || p_prop == StringName("tile_mode")) {
+ _on_tile_list_selected(get_current_tile());
+ workspace->update();
+ preview->set_texture(tileset->tile_get_texture(get_current_tile()));
+ preview->set_modulate(tileset->tile_get_modulate(get_current_tile()));
+ preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE)
+ property_editor->show();
+ else
+ property_editor->hide();
+ texture_region_editor->_edit_region();
+ update_tile_list_icon();
+ } else if (p_prop == StringName("autotile")) {
+ workspace->update();
+ }
}
-AutotileEditor::AutotileEditor(EditorNode *p_editor) {
-
- editor = p_editor;
+void TileSetEditor::initialize_bottom_editor() {
//Side Panel
side_panel = memnew(Control);
- side_panel->set_name("Autotiles");
+ side_panel->set_name("Tile Set");
VSplitContainer *split = memnew(VSplitContainer);
side_panel->add_child(split);
split->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- autotile_list = memnew(ItemList);
- autotile_list->set_v_size_flags(SIZE_EXPAND_FILL);
- autotile_list->set_h_size_flags(SIZE_EXPAND_FILL);
- autotile_list->set_custom_minimum_size(Size2(10, 200));
- autotile_list->connect("item_selected", this, "_on_autotile_selected");
- split->add_child(autotile_list);
+ tile_list = memnew(ItemList);
+ tile_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_list->set_custom_minimum_size(Size2(10, 200));
+ tile_list->connect("item_selected", this, "_on_tile_list_selected");
+ split->add_child(tile_list);
property_editor = memnew(PropertyEditor);
property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -342,25 +319,29 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
property_editor->set_custom_minimum_size(Size2(10, 70));
split->add_child(property_editor);
- helper = memnew(AutotileEditorHelper(this));
+ helper = memnew(TileSetEditorHelper(this));
property_editor->call_deferred("edit", helper);
+ helper->add_change_receptor(this);
//Editor
+ //Bottom Panel
+ bottom_panel = memnew(Control);
+ bottom_panel->set_name("Tile Set Bottom Editor");
dragging_point = -1;
creating_shape = false;
snap_step = Vector2(32, 32);
- set_custom_minimum_size(Size2(0, 150));
+ bottom_panel->set_custom_minimum_size(Size2(0, 150));
VBoxContainer *main_vb = memnew(VBoxContainer);
- add_child(main_vb);
+ bottom_panel->add_child(main_vb);
main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE);
HBoxContainer *tool_hb = memnew(HBoxContainer);
Ref<ButtonGroup> g(memnew(ButtonGroup));
- String label[EDITMODE_MAX] = { "Icon", "Bitmask", "Collision", "Occlusion", "Navigation", "Priority" };
+ String label[EDITMODE_MAX] = { "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon" };
for (int i = 0; i < (int)EDITMODE_MAX; i++) {
tool_editmode[i] = memnew(Button);
@@ -372,7 +353,8 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
tool_editmode[i]->connect("pressed", this, "_on_edit_mode_changed", args);
tool_hb->add_child(tool_editmode[i]);
}
- tool_editmode[EDITMODE_ICON]->set_pressed(true);
+ tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
+ edit_mode = EDITMODE_COLLISION;
main_vb->add_child(tool_hb);
main_vb->add_child(memnew(HSeparator));
@@ -386,15 +368,17 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
Ref<ButtonGroup> tg(memnew(ButtonGroup));
+ Vector<Variant> p;
tools[TOOL_SELECT] = memnew(ToolButton);
tool_containers[TOOLBAR_DUMMY]->add_child(tools[TOOL_SELECT]);
tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings."));
tools[TOOL_SELECT]->set_toggle_mode(true);
tools[TOOL_SELECT]->set_button_group(tg);
tools[TOOL_SELECT]->set_pressed(true);
+ p.push_back((int)TOOL_SELECT);
+ tools[TOOL_SELECT]->connect("pressed", this, "_on_tool_clicked", p);
tool_containers[TOOLBAR_DUMMY]->show();
- Vector<Variant> p;
tools[BITMASK_COPY] = memnew(ToolButton);
p.push_back((int)BITMASK_COPY);
tools[BITMASK_COPY]->connect("pressed", this, "_on_tool_clicked", p);
@@ -420,7 +404,7 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
p.push_back((int)SHAPE_DELETE);
tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", p);
tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_DELETE]);
- tool_containers[TOOLBAR_SHAPE]->add_change_receptor(memnew(VSeparator));
+ tool_containers[TOOLBAR_SHAPE]->add_child(memnew(VSeparator));
tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton);
tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true);
tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true);
@@ -507,6 +491,8 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
spin_priority->hide();
toolbar->add_child(spin_priority);
+ tool_containers[TOOLBAR_SHAPE]->show();
+
Control *separator = memnew(Control);
separator->set_h_size_flags(SIZE_EXPAND_FILL);
toolbar->add_child(separator);
@@ -536,10 +522,15 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
workspace_container = memnew(Control);
scroll->add_child(workspace_container);
+ workspace_overlay = memnew(Control);
+ workspace_overlay->connect("draw", this, "_on_workspace_overlay_draw");
+ workspace_container->add_child(workspace_overlay);
+
workspace = memnew(Control);
workspace->connect("draw", this, "_on_workspace_draw");
workspace->connect("gui_input", this, "_on_workspace_input");
- workspace_container->add_child(workspace);
+ workspace->set_draw_behind_parent(true);
+ workspace_overlay->add_child(workspace);
preview = memnew(Sprite);
workspace->add_child(preview);
@@ -548,66 +539,64 @@ AutotileEditor::AutotileEditor(EditorNode *p_editor) {
preview->set_region(true);
}
-void AutotileEditor::_bind_methods() {
-
- ClassDB::bind_method("_on_autotile_selected", &AutotileEditor::_on_autotile_selected);
- ClassDB::bind_method("_on_edit_mode_changed", &AutotileEditor::_on_edit_mode_changed);
- ClassDB::bind_method("_on_workspace_draw", &AutotileEditor::_on_workspace_draw);
- ClassDB::bind_method("_on_workspace_input", &AutotileEditor::_on_workspace_input);
- ClassDB::bind_method("_on_tool_clicked", &AutotileEditor::_on_tool_clicked);
- ClassDB::bind_method("_on_priority_changed", &AutotileEditor::_on_priority_changed);
- ClassDB::bind_method("_on_grid_snap_toggled", &AutotileEditor::_on_grid_snap_toggled);
- ClassDB::bind_method("_set_snap_step_x", &AutotileEditor::_set_snap_step_x);
- ClassDB::bind_method("_set_snap_step_y", &AutotileEditor::_set_snap_step_y);
- ClassDB::bind_method("_set_snap_off_x", &AutotileEditor::_set_snap_off_x);
- ClassDB::bind_method("_set_snap_off_y", &AutotileEditor::_set_snap_off_y);
- ClassDB::bind_method("_set_snap_sep_x", &AutotileEditor::_set_snap_sep_x);
- ClassDB::bind_method("_set_snap_sep_y", &AutotileEditor::_set_snap_sep_y);
-}
+TileSetEditor::TileSetEditor(EditorNode *p_editor) {
-void AutotileEditor::_notification(int p_what) {
+ menu = memnew(MenuButton);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu);
+ menu->hide();
+ menu->set_text(TTR("Tile Set"));
+ menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
+ menu->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM);
+ menu->get_popup()->add_separator();
+ menu->get_popup()->add_item(TTR("Create from Scene"), MENU_OPTION_CREATE_FROM_SCENE);
+ menu->get_popup()->add_item(TTR("Merge from Scene"), MENU_OPTION_MERGE_FROM_SCENE);
+ menu->get_popup()->connect("id_pressed", this, "_menu_cbk");
+ editor = p_editor;
+ cd = memnew(ConfirmationDialog);
+ add_child(cd);
+ cd->get_ok()->connect("pressed", this, "_menu_confirm");
- if (p_what == NOTIFICATION_ENTER_TREE) {
- tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
- tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons"));
- tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons"));
- tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons"));
- tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons"));
- tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons"));
- tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons"));
- tools[SHAPE_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons"));
- tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons"));
- tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons"));
- tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons"));
- }
-}
+ nd = memnew(EditorNameDialog);
+ add_child(nd);
+ nd->set_hide_on_ok(true);
+ nd->get_line_edit()->set_margin(MARGIN_TOP, 28);
+ nd->connect("name_confirmed", this, "_name_dialog_confirm");
-void AutotileEditor::_changed_callback(Object *p_changed, const char *p_prop) {
- if (p_prop == StringName("texture") || p_prop == StringName("is_autotile")) {
- edit(tile_set.ptr());
- autotile_list->update();
- workspace->update();
- }
+ err_dialog = memnew(AcceptDialog);
+ add_child(err_dialog);
+ err_dialog->set_title(TTR("Error"));
+
+ draw_handles = false;
+
+ initialize_bottom_editor();
}
-void AutotileEditor::_on_autotile_selected(int p_index) {
+TileSetEditor::~TileSetEditor() {
+ if (helper)
+ memdelete(helper);
+}
+void TileSetEditor::_on_tile_list_selected(int p_index) {
if (get_current_tile() >= 0) {
current_item_index = p_index;
- preview->set_texture(tile_set->tile_get_texture(get_current_tile()));
- preview->set_region_rect(tile_set->tile_get_region(get_current_tile()));
- workspace->set_custom_minimum_size(tile_set->tile_get_region(get_current_tile()).size);
+ preview->set_texture(tileset->tile_get_texture(get_current_tile()));
+ preview->set_modulate(tileset->tile_get_modulate(get_current_tile()));
+ preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
+ workspace->set_custom_minimum_size(tileset->tile_get_region(get_current_tile()).size);
+ update_workspace_tile_mode();
} else {
current_item_index = -1;
preview->set_texture(NULL);
workspace->set_custom_minimum_size(Size2i());
}
+ texture_region_editor->selected_tile = get_current_tile();
+ texture_region_editor->_edit_region();
+ helper->selected_tile = get_current_tile();
helper->_change_notify("");
workspace->update();
}
-void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) {
-
+void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) {
edit_mode = (EditMode)p_edit_mode;
switch (edit_mode) {
case EDITMODE_BITMASK: {
@@ -627,7 +616,6 @@ void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) {
tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile."));
spin_priority->hide();
- current_shape = PoolVector2Array();
select_coord(edited_shape_coord);
} break;
default: {
@@ -646,17 +634,17 @@ void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) {
workspace->update();
}
-void AutotileEditor::_on_workspace_draw() {
+void TileSetEditor::_on_workspace_draw() {
- if (get_current_tile() >= 0 && !tile_set.is_null()) {
- int spacing = tile_set->autotile_get_spacing(get_current_tile());
- Vector2 size = tile_set->autotile_get_size(get_current_tile());
- Rect2i region = tile_set->tile_get_region(get_current_tile());
+ if (get_current_tile() >= 0 && !tileset.is_null()) {
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->autotile_get_size(get_current_tile());
+ Rect2i region = tileset->tile_get_region(get_current_tile());
Color c(0.347214, 0.722656, 0.617063);
switch (edit_mode) {
case EDITMODE_ICON: {
- Vector2 coord = tile_set->autotile_get_icon_coordinate(get_current_tile());
+ Vector2 coord = tileset->autotile_get_icon_coordinate(get_current_tile());
draw_highlight_tile(coord);
} break;
case EDITMODE_BITMASK: {
@@ -665,8 +653,8 @@ void AutotileEditor::_on_workspace_draw() {
for (float y = 0; y < region.size.y / (spacing + size.y); y++) {
Vector2 coord(x, y);
Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
- uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord);
- if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
+ if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
if (mask & TileSet::BIND_TOPLEFT) {
workspace->draw_rect(Rect2(anchor, size / 2), c);
}
@@ -679,7 +667,7 @@ void AutotileEditor::_on_workspace_draw() {
if (mask & TileSet::BIND_BOTTOMRIGHT) {
workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c);
}
- } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
if (mask & TileSet::BIND_TOPLEFT) {
workspace->draw_rect(Rect2(anchor, size / 3), c);
}
@@ -714,19 +702,21 @@ void AutotileEditor::_on_workspace_draw() {
case EDITMODE_COLLISION:
case EDITMODE_OCCLUSION:
case EDITMODE_NAVIGATION: {
- Vector2 coord = edited_shape_coord;
- draw_highlight_tile(coord);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ Vector2 coord = edited_shape_coord;
+ draw_highlight_tile(coord);
+ }
draw_polygon_shapes();
draw_grid_snap();
} break;
case EDITMODE_PRIORITY: {
- spin_priority->set_value(tile_set->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
- uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), edited_shape_coord);
+ spin_priority->set_value(tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
+ uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord);
Vector<Vector2> queue_others;
int total = 0;
- for (Map<Vector2, uint16_t>::Element *E = tile_set->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
+ for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
if (E->value() == mask) {
- total += tile_set->autotile_get_subtile_priority(get_current_tile(), E->key());
+ total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key());
if (E->key() != edited_shape_coord) {
queue_others.push_back(E->key());
}
@@ -737,53 +727,69 @@ void AutotileEditor::_on_workspace_draw() {
} break;
}
- float j = -size.x; //make sure to draw at 0
- while (j < region.size.x) {
- j += size.x;
- if (spacing <= 0) {
- workspace->draw_line(Point2(j, 0), Point2(j, region.size.y), c);
- } else {
- workspace->draw_rect(Rect2(Point2(j, 0), Size2(spacing, region.size.y)), c);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ float j = -size.x; //make sure to draw at 0
+ while (j < region.size.x) {
+ j += size.x;
+ if (spacing <= 0) {
+ workspace->draw_line(Point2(j, 0), Point2(j, region.size.y), c);
+ } else {
+ workspace->draw_rect(Rect2(Point2(j, 0), Size2(spacing, region.size.y)), c);
+ }
+ j += spacing;
}
- j += spacing;
- }
- j = -size.y; //make sure to draw at 0
- while (j < region.size.y) {
- j += size.y;
- if (spacing <= 0) {
- workspace->draw_line(Point2(0, j), Point2(region.size.x, j), c);
- } else {
- workspace->draw_rect(Rect2(Point2(0, j), Size2(region.size.x, spacing)), c);
+ j = -size.y; //make sure to draw at 0
+ while (j < region.size.y) {
+ j += size.y;
+ if (spacing <= 0) {
+ workspace->draw_line(Point2(0, j), Point2(region.size.x, j), c);
+ } else {
+ workspace->draw_rect(Rect2(Point2(0, j), Size2(region.size.x, spacing)), c);
+ }
+ j += spacing;
}
- j += spacing;
}
}
+ workspace_overlay->update();
+}
+
+void TileSetEditor::_on_workspace_overlay_draw() {
+
+ int t_id = get_current_tile();
+ if (t_id < 0 || !draw_handles)
+ return;
+
+ Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5);
+ }
}
-#define MIN_DISTANCE_SQUARED 10
-void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
+#define MIN_DISTANCE_SQUARED 6
+void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
- if (get_current_tile() >= 0 && !tile_set.is_null()) {
+ if (get_current_tile() >= 0 && !tileset.is_null()) {
Ref<InputEventMouseButton> mb = p_ie;
Ref<InputEventMouseMotion> mm = p_ie;
static bool dragging;
static bool erasing;
- int spacing = tile_set->autotile_get_spacing(get_current_tile());
- Vector2 size = tile_set->autotile_get_size(get_current_tile());
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->autotile_get_size(get_current_tile());
switch (edit_mode) {
case EDITMODE_ICON: {
if (mb.is_valid()) {
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
- tile_set->autotile_set_icon_coordinate(get_current_tile(), coord);
- Rect2 region = tile_set->tile_get_region(get_current_tile());
+ tileset->autotile_set_icon_coordinate(get_current_tile(), coord);
+ Rect2 region = tileset->tile_get_region(get_current_tile());
region.size = size;
coord.x *= (spacing + size.x);
coord.y *= (spacing + size.y);
region.position += coord;
- autotile_list->set_item_icon_region(current_item_index, region);
+ tile_list->set_item_icon_region(current_item_index, region);
workspace->update();
}
}
@@ -800,8 +806,8 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
pos = mb->get_position() - pos;
- uint16_t bit;
- if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ uint16_t bit = 0;
+ if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
if (pos.x < size.x / 2) {
if (pos.y < size.y / 2) {
bit = TileSet::BIND_TOPLEFT;
@@ -815,7 +821,7 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
bit = TileSet::BIND_BOTTOMRIGHT;
}
}
- } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
if (pos.x < size.x / 3) {
if (pos.y < size.y / 3) {
bit = TileSet::BIND_TOPLEFT;
@@ -842,13 +848,13 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
}
- uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord);
+ uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
if (erasing) {
mask &= ~bit;
} else {
mask |= bit;
}
- tile_set->autotile_set_bitmask(get_current_tile(), coord, mask);
+ tileset->autotile_set_bitmask(get_current_tile(), coord, mask);
workspace->update();
}
} else {
@@ -863,8 +869,8 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
Vector2 coord((int)(mm->get_position().x / (spacing + size.x)), (int)(mm->get_position().y / (spacing + size.y)));
Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
pos = mm->get_position() - pos;
- uint16_t bit;
- if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ uint16_t bit = 0;
+ if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
if (pos.x < size.x / 2) {
if (pos.y < size.y / 2) {
bit = TileSet::BIND_TOPLEFT;
@@ -878,7 +884,7 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
bit = TileSet::BIND_BOTTOMRIGHT;
}
}
- } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ } else if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
if (pos.x < size.x / 3) {
if (pos.y < size.y / 3) {
bit = TileSet::BIND_TOPLEFT;
@@ -905,13 +911,13 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
}
- uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord);
+ uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
if (erasing) {
mask &= ~bit;
} else {
mask |= bit;
}
- tile_set->autotile_set_bitmask(get_current_tile(), coord, mask);
+ tileset->autotile_set_bitmask(get_current_tile(), coord, mask);
workspace->update();
}
}
@@ -920,9 +926,12 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
case EDITMODE_OCCLUSION:
case EDITMODE_NAVIGATION:
case EDITMODE_PRIORITY: {
- Vector2 shape_anchor = edited_shape_coord;
- shape_anchor.x *= (size.x + spacing);
- shape_anchor.y *= (size.y + spacing);
+ Vector2 shape_anchor = Vector2(0, 0);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ shape_anchor = edited_shape_coord;
+ shape_anchor.x *= (size.x + spacing);
+ shape_anchor.y *= (size.y + spacing);
+ }
if (tools[TOOL_SELECT]->is_pressed()) {
if (mb.is_valid()) {
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
@@ -935,23 +944,25 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
}
- Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
- if (edited_shape_coord != coord) {
- edited_shape_coord = coord;
- edited_occlusion_shape = tile_set->autotile_get_light_occluder(get_current_tile(), edited_shape_coord);
- edited_navigation_shape = tile_set->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord);
- Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile());
- bool found_collision_shape = false;
- for (int i = 0; i < sd.size(); i++) {
- if (sd[i].autotile_coord == coord) {
- edited_collision_shape = sd[i].shape;
- found_collision_shape = true;
- break;
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
+ if (edited_shape_coord != coord) {
+ edited_shape_coord = coord;
+ edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord);
+ edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord);
+ Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
+ bool found_collision_shape = false;
+ for (int i = 0; i < sd.size(); i++) {
+ if (sd[i].autotile_coord == coord) {
+ edited_collision_shape = sd[i].shape;
+ found_collision_shape = true;
+ break;
+ }
}
+ if (!found_collision_shape)
+ edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL);
+ select_coord(edited_shape_coord);
}
- if (!found_collision_shape)
- edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL);
- select_coord(edited_shape_coord);
}
workspace->update();
} else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
@@ -1019,6 +1030,7 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
} else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) {
+
if (mb.is_valid()) {
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
Vector2 pos = mb->get_position();
@@ -1039,14 +1051,14 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
int t_id = get_current_tile();
if (t_id >= 0) {
if (edit_mode == EDITMODE_COLLISION) {
- Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(t_id);
+ Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id);
for (int i = 0; i < sd.size(); i++) {
- if (sd[i].autotile_coord == edited_shape_coord) {
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || sd[i].autotile_coord == edited_shape_coord) {
Ref<ConvexPolygonShape2D> shape = sd[i].shape;
if (!shape.is_null()) {
sd.remove(i);
- tile_set->tile_set_shapes(get_current_tile(), sd);
+ tileset->tile_set_shapes(get_current_tile(), sd);
edited_collision_shape = Ref<Shape2D>();
workspace->update();
}
@@ -1054,25 +1066,30 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
} else if (edit_mode == EDITMODE_OCCLUSION) {
- Map<Vector2, Ref<OccluderPolygon2D> > map = tile_set->autotile_get_light_oclusion_map(t_id);
- for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) {
- if (E->key() == edited_shape_coord) {
- tile_set->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
- break;
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id);
+ for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) {
+ if (E->key() == edited_shape_coord) {
+ tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
+ break;
+ }
}
- }
+ } else
+ tileset->tile_set_light_occluder(t_id, Ref<OccluderPolygon2D>());
edited_occlusion_shape = Ref<OccluderPolygon2D>();
workspace->update();
} else if (edit_mode == EDITMODE_NAVIGATION) {
- Map<Vector2, Ref<NavigationPolygon> > map = tile_set->autotile_get_navigation_map(t_id);
- for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) {
- if (E->key() == edited_shape_coord) {
- tile_set->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord);
- break;
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id);
+ for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) {
+ if (E->key() == edited_shape_coord) {
+ tileset->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord);
+ break;
+ }
}
- }
-
+ } else
+ tileset->tile_set_navigation_polygon(t_id, Ref<NavigationPolygon>());
edited_navigation_shape = Ref<NavigationPolygon>();
workspace->update();
}
@@ -1108,17 +1125,17 @@ void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
}
-void AutotileEditor::_on_tool_clicked(int p_tool) {
+void TileSetEditor::_on_tool_clicked(int p_tool) {
if (p_tool == BITMASK_COPY) {
- bitmask_map_copy = tile_set->autotile_get_bitmask_map(get_current_tile());
+ bitmask_map_copy = tileset->autotile_get_bitmask_map(get_current_tile());
} else if (p_tool == BITMASK_PASTE) {
- tile_set->autotile_clear_bitmask_map(get_current_tile());
+ tileset->autotile_clear_bitmask_map(get_current_tile());
for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) {
- tile_set->autotile_set_bitmask(get_current_tile(), E->key(), E->value());
+ tileset->autotile_set_bitmask(get_current_tile(), E->key(), E->value());
}
workspace->update();
} else if (p_tool == BITMASK_CLEAR) {
- tile_set->autotile_clear_bitmask_map(get_current_tile());
+ tileset->autotile_clear_bitmask_map(get_current_tile());
workspace->update();
} else if (p_tool == SHAPE_DELETE) {
if (creating_shape) {
@@ -1129,8 +1146,8 @@ void AutotileEditor::_on_tool_clicked(int p_tool) {
switch (edit_mode) {
case EDITMODE_COLLISION: {
if (!edited_collision_shape.is_null()) {
- Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile());
- int index;
+ Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
+ int index = -1;
for (int i = 0; i < sd.size(); i++) {
if (sd[i].shape == edited_collision_shape) {
index = i;
@@ -1139,7 +1156,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) {
}
if (index >= 0) {
sd.remove(index);
- tile_set->tile_set_shapes(get_current_tile(), sd);
+ tileset->tile_set_shapes(get_current_tile(), sd);
edited_collision_shape = Ref<Shape2D>();
current_shape.resize(0);
workspace->update();
@@ -1148,7 +1165,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) {
} break;
case EDITMODE_NAVIGATION: {
if (!edited_navigation_shape.is_null()) {
- tile_set->autotile_set_navigation_polygon(get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord);
+ tileset->autotile_set_navigation_polygon(get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord);
edited_navigation_shape = Ref<NavigationPolygon>();
current_shape.resize(0);
workspace->update();
@@ -1156,7 +1173,7 @@ void AutotileEditor::_on_tool_clicked(int p_tool) {
} break;
case EDITMODE_OCCLUSION: {
if (!edited_occlusion_shape.is_null()) {
- tile_set->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
+ tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
edited_occlusion_shape = Ref<OccluderPolygon2D>();
current_shape.resize(0);
workspace->update();
@@ -1170,24 +1187,34 @@ void AutotileEditor::_on_tool_clicked(int p_tool) {
scale /= 2;
workspace->set_scale(Vector2(scale, scale));
workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale);
+ workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale);
}
} else if (p_tool == ZOOM_1) {
workspace->set_scale(Vector2(1, 1));
workspace_container->set_custom_minimum_size(preview->get_region_rect().size);
+ workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size);
} else if (p_tool == ZOOM_IN) {
float scale = workspace->get_scale().x;
scale *= 2;
workspace->set_scale(Vector2(scale, scale));
workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale);
+ workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale);
+ } else if (p_tool == TOOL_SELECT) {
+ if (creating_shape) {
+ //Cancel Creation
+ creating_shape = false;
+ current_shape.resize(0);
+ workspace->update();
+ }
}
}
-void AutotileEditor::_on_priority_changed(float val) {
- tile_set->autotile_set_subtile_priority(get_current_tile(), edited_shape_coord, (int)val);
+void TileSetEditor::_on_priority_changed(float val) {
+ tileset->autotile_set_subtile_priority(get_current_tile(), edited_shape_coord, (int)val);
workspace->update();
}
-void AutotileEditor::_on_grid_snap_toggled(bool p_val) {
+void TileSetEditor::_on_grid_snap_toggled(bool p_val) {
if (p_val)
hb_grid->show();
else
@@ -1195,40 +1222,40 @@ void AutotileEditor::_on_grid_snap_toggled(bool p_val) {
workspace->update();
}
-void AutotileEditor::_set_snap_step_x(float p_val) {
+void TileSetEditor::_set_snap_step_x(float p_val) {
snap_step.x = p_val;
workspace->update();
}
-void AutotileEditor::_set_snap_step_y(float p_val) {
+void TileSetEditor::_set_snap_step_y(float p_val) {
snap_step.y = p_val;
workspace->update();
}
-void AutotileEditor::_set_snap_off_x(float p_val) {
+void TileSetEditor::_set_snap_off_x(float p_val) {
snap_offset.x = p_val;
workspace->update();
}
-void AutotileEditor::_set_snap_off_y(float p_val) {
+void TileSetEditor::_set_snap_off_y(float p_val) {
snap_offset.y = p_val;
workspace->update();
}
-void AutotileEditor::_set_snap_sep_x(float p_val) {
+void TileSetEditor::_set_snap_sep_x(float p_val) {
snap_separation.x = p_val;
workspace->update();
}
-void AutotileEditor::_set_snap_sep_y(float p_val) {
+void TileSetEditor::_set_snap_sep_y(float p_val) {
snap_separation.y = p_val;
workspace->update();
}
-void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) {
+void TileSetEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) {
- Vector2 size = tile_set->autotile_get_size(get_current_tile());
- int spacing = tile_set->autotile_get_spacing(get_current_tile());
- Rect2 region = tile_set->tile_get_region(get_current_tile());
+ Vector2 size = tileset->autotile_get_size(get_current_tile());
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Rect2 region = tileset->tile_get_region(get_current_tile());
coord.x *= (size.x + spacing);
coord.y *= (size.y + spacing);
workspace->draw_rect(Rect2(0, 0, region.size.x, coord.y), Color(0.5, 0.5, 0.5, 0.5));
@@ -1246,7 +1273,7 @@ void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &o
}
}
-void AutotileEditor::draw_grid_snap() {
+void TileSetEditor::draw_grid_snap() {
if (tools[SHAPE_GRID_SNAP]->is_pressed()) {
Color grid_color = Color(0.39, 0, 1, 0.2f);
Size2 s = workspace->get_size();
@@ -1287,27 +1314,33 @@ void AutotileEditor::draw_grid_snap() {
}
}
-void AutotileEditor::draw_polygon_shapes() {
+void TileSetEditor::draw_polygon_shapes() {
int t_id = get_current_tile();
if (t_id < 0)
return;
+ draw_handles = false;
+
switch (edit_mode) {
case EDITMODE_COLLISION: {
- Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(t_id);
+ Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id);
for (int i = 0; i < sd.size(); i++) {
- Vector2 coord = sd[i].autotile_coord;
- Vector2 anchor = tile_set->autotile_get_size(t_id);
- anchor.x += tile_set->autotile_get_spacing(t_id);
- anchor.y += tile_set->autotile_get_spacing(t_id);
- anchor.x *= coord.x;
- anchor.y *= coord.y;
+ Vector2 coord = Vector2(0, 0);
+ Vector2 anchor = Vector2(0, 0);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
+ coord = sd[i].autotile_coord;
+ anchor = tileset->autotile_get_size(t_id);
+ anchor.x += tileset->autotile_get_spacing(t_id);
+ anchor.y += tileset->autotile_get_spacing(t_id);
+ anchor.x *= coord.x;
+ anchor.y *= coord.y;
+ }
Ref<ConvexPolygonShape2D> shape = sd[i].shape;
if (shape.is_valid()) {
Color c_bg;
Color c_border;
- if (coord == edited_shape_coord && sd[i].shape == edited_collision_shape) {
+ if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || coord == edited_shape_coord) && sd[i].shape == edited_collision_shape) {
c_bg = Color(0, 1, 1, 0.5);
c_border = Color(0, 1, 1);
} else {
@@ -1330,62 +1363,82 @@ void AutotileEditor::draw_polygon_shapes() {
if (polygon.size() > 2) {
workspace->draw_polygon(polygon, colors);
}
- if (coord == edited_shape_coord) {
+ if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
for (int j = 0; j < shape->get_points().size() - 1; j++) {
workspace->draw_line(shape->get_points()[j] + anchor, shape->get_points()[j + 1] + anchor, c_border, 1, true);
}
if (shape == edited_collision_shape) {
- for (int j = 0; j < current_shape.size(); j++) {
- workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0, 0.7f));
- }
+ draw_handles = true;
}
}
}
}
} break;
case EDITMODE_OCCLUSION: {
- Map<Vector2, Ref<OccluderPolygon2D> > map = tile_set->autotile_get_light_oclusion_map(t_id);
- for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) {
- Vector2 coord = E->key();
- Vector2 anchor = tile_set->autotile_get_size(t_id);
- anchor.x += tile_set->autotile_get_spacing(t_id);
- anchor.y += tile_set->autotile_get_spacing(t_id);
- anchor.x *= coord.x;
- anchor.y *= coord.y;
- Ref<OccluderPolygon2D> shape = E->value();
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ Ref<OccluderPolygon2D> shape = edited_occlusion_shape;
if (shape.is_valid()) {
- Color c_bg;
- Color c_border;
- if (coord == edited_shape_coord && shape == edited_occlusion_shape) {
- c_bg = Color(0, 1, 1, 0.5);
- c_border = Color(0, 1, 1);
- } else {
- c_bg = Color(0.9, 0.7, 0.07, 0.5);
- c_border = Color(0.9, 0.7, 0.07, 1);
- }
+ Color c_bg = Color(0, 1, 1, 0.5);
+ Color c_border = Color(0, 1, 1);
+
Vector<Vector2> polygon;
Vector<Color> colors;
- if (shape == edited_occlusion_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else {
- for (int j = 0; j < shape->get_polygon().size(); j++) {
- polygon.push_back(shape->get_polygon()[j] + anchor);
- colors.push_back(c_bg);
- }
+ for (int j = 0; j < shape->get_polygon().size(); j++) {
+ polygon.push_back(shape->get_polygon()[j]);
+ colors.push_back(c_bg);
}
workspace->draw_polygon(polygon, colors);
- if (coord == edited_shape_coord) {
- for (int j = 0; j < shape->get_polygon().size() - 1; j++) {
- workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true);
+
+ for (int j = 0; j < shape->get_polygon().size() - 1; j++) {
+ workspace->draw_line(shape->get_polygon()[j], shape->get_polygon()[j + 1], c_border, 1, true);
+ }
+ workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1], shape->get_polygon()[0], c_border, 1, true);
+ if (shape == edited_occlusion_shape) {
+ draw_handles = true;
+ }
+ }
+ } else {
+ Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id);
+ for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) {
+ Vector2 coord = E->key();
+ Vector2 anchor = tileset->autotile_get_size(t_id);
+ anchor.x += tileset->autotile_get_spacing(t_id);
+ anchor.y += tileset->autotile_get_spacing(t_id);
+ anchor.x *= coord.x;
+ anchor.y *= coord.y;
+ Ref<OccluderPolygon2D> shape = E->value();
+ if (shape.is_valid()) {
+ Color c_bg;
+ Color c_border;
+ if (coord == edited_shape_coord && shape == edited_occlusion_shape) {
+ c_bg = Color(0, 1, 1, 0.5);
+ c_border = Color(0, 1, 1);
+ } else {
+ c_bg = Color(0.9, 0.7, 0.07, 0.5);
+ c_border = Color(0.9, 0.7, 0.07, 1);
}
- workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true);
- if (shape == edited_occlusion_shape) {
+ Vector<Vector2> polygon;
+ Vector<Color> colors;
+ if (shape == edited_occlusion_shape && current_shape.size() > 2) {
for (int j = 0; j < current_shape.size(); j++) {
- workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0));
+ polygon.push_back(current_shape[j]);
+ colors.push_back(c_bg);
+ }
+ } else {
+ for (int j = 0; j < shape->get_polygon().size(); j++) {
+ polygon.push_back(shape->get_polygon()[j] + anchor);
+ colors.push_back(c_bg);
+ }
+ }
+ workspace->draw_polygon(polygon, colors);
+ if (coord == edited_shape_coord) {
+ for (int j = 0; j < shape->get_polygon().size() - 1; j++) {
+ workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true);
+ }
+ workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true);
+ if (shape == edited_occlusion_shape) {
+ draw_handles = true;
}
}
}
@@ -1393,49 +1446,77 @@ void AutotileEditor::draw_polygon_shapes() {
}
} break;
case EDITMODE_NAVIGATION: {
- Map<Vector2, Ref<NavigationPolygon> > map = tile_set->autotile_get_navigation_map(t_id);
- for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) {
- Vector2 coord = E->key();
- Vector2 anchor = tile_set->autotile_get_size(t_id);
- anchor.x += tile_set->autotile_get_spacing(t_id);
- anchor.y += tile_set->autotile_get_spacing(t_id);
- anchor.x *= coord.x;
- anchor.y *= coord.y;
- Ref<NavigationPolygon> shape = E->value();
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ Ref<NavigationPolygon> shape = edited_navigation_shape;
+
if (shape.is_valid()) {
- Color c_bg;
- Color c_border;
- if (coord == edited_shape_coord && shape == edited_navigation_shape) {
- c_bg = Color(0, 1, 1, 0.5);
- c_border = Color(0, 1, 1);
- } else {
- c_bg = Color(0.9, 0.7, 0.07, 0.5);
- c_border = Color(0.9, 0.7, 0.07, 1);
- }
+ Color c_bg = Color(0, 1, 1, 0.5);
+ Color c_border = Color(0, 1, 1);
+
Vector<Vector2> polygon;
Vector<Color> colors;
- if (shape == edited_navigation_shape && current_shape.size() > 2) {
- for (int j = 0; j < current_shape.size(); j++) {
- polygon.push_back(current_shape[j]);
- colors.push_back(c_bg);
- }
- } else if (shape->get_polygon_count() > 0) {
+
+ PoolVector<Vector2> vertices = shape->get_vertices();
+ for (int j = 0; j < shape->get_polygon(0).size(); j++) {
+ polygon.push_back(vertices[shape->get_polygon(0)[j]]);
+ colors.push_back(c_bg);
+ }
+ workspace->draw_polygon(polygon, colors);
+
+ if (shape->get_polygon_count() > 0) {
PoolVector<Vector2> vertices = shape->get_vertices();
- for (int j = 0; j < shape->get_polygon(0).size(); j++) {
- polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
- colors.push_back(c_bg);
+ for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) {
+ workspace->draw_line(vertices[shape->get_polygon(0)[j]], vertices[shape->get_polygon(0)[j + 1]], c_border, 1, true);
+ }
+ if (shape == edited_navigation_shape) {
+ draw_handles = true;
}
}
- workspace->draw_polygon(polygon, colors);
- if (coord == edited_shape_coord) {
- if (shape->get_polygon_count() > 0) {
+ }
+
+ } else {
+ Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id);
+ for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) {
+ Vector2 coord = E->key();
+ Vector2 anchor = tileset->autotile_get_size(t_id);
+ anchor.x += tileset->autotile_get_spacing(t_id);
+ anchor.y += tileset->autotile_get_spacing(t_id);
+ anchor.x *= coord.x;
+ anchor.y *= coord.y;
+ Ref<NavigationPolygon> shape = E->value();
+ if (shape.is_valid()) {
+ Color c_bg;
+ Color c_border;
+ if (coord == edited_shape_coord && shape == edited_navigation_shape) {
+ c_bg = Color(0, 1, 1, 0.5);
+ c_border = Color(0, 1, 1);
+ } else {
+ c_bg = Color(0.9, 0.7, 0.07, 0.5);
+ c_border = Color(0.9, 0.7, 0.07, 1);
+ }
+ Vector<Vector2> polygon;
+ Vector<Color> colors;
+ if (shape == edited_navigation_shape && current_shape.size() > 2) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ polygon.push_back(current_shape[j]);
+ colors.push_back(c_bg);
+ }
+ } else if (shape->get_polygon_count() > 0) {
PoolVector<Vector2> vertices = shape->get_vertices();
- for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) {
- workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true);
+ for (int j = 0; j < shape->get_polygon(0).size(); j++) {
+ polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
+ colors.push_back(c_bg);
}
- if (shape == edited_navigation_shape) {
- for (int j = 0; j < current_shape.size(); j++) {
- workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0));
+ }
+ workspace->draw_polygon(polygon, colors);
+ if (coord == edited_shape_coord) {
+ if (shape->get_polygon_count() > 0) {
+ PoolVector<Vector2> vertices = shape->get_vertices();
+ for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) {
+ workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true);
+ }
+ if (shape == edited_navigation_shape) {
+ draw_handles = true;
}
}
}
@@ -1452,7 +1533,7 @@ void AutotileEditor::draw_polygon_shapes() {
}
}
-void AutotileEditor::close_shape(const Vector2 &shape_anchor) {
+void TileSetEditor::close_shape(const Vector2 &shape_anchor) {
creating_shape = false;
@@ -1477,7 +1558,11 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) {
shape->set_points(segments);
- tile_set->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE)
+ tileset->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord);
+ else
+ tileset->tile_set_shape(get_current_tile(), 0, shape);
+
edited_collision_shape = shape;
}
@@ -1497,7 +1582,10 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) {
w = PoolVector<Vector2>::Write();
shape->set_polygon(polygon);
- tile_set->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE)
+ tileset->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord);
+ else
+ tileset->tile_set_light_occluder(get_current_tile(), shape);
edited_occlusion_shape = shape;
tools[TOOL_SELECT]->set_pressed(true);
workspace->update();
@@ -1517,55 +1605,99 @@ void AutotileEditor::close_shape(const Vector2 &shape_anchor) {
w = PoolVector<Vector2>::Write();
shape->set_vertices(polygon);
shape->add_polygon(indices);
- tile_set->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord);
+
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE)
+ tileset->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord);
+ else
+ tileset->tile_set_navigation_polygon(get_current_tile(), shape);
edited_navigation_shape = shape;
tools[TOOL_SELECT]->set_pressed(true);
workspace->update();
}
+ tileset->_change_notify("");
}
-void AutotileEditor::select_coord(const Vector2 &coord) {
- int spacing = tile_set->autotile_get_spacing(get_current_tile());
- Vector2 size = tile_set->autotile_get_size(get_current_tile());
- Vector2 shape_anchor = coord;
- shape_anchor.x *= (size.x + spacing);
- shape_anchor.y *= (size.y + spacing);
- if (edit_mode == EDITMODE_COLLISION) {
- current_shape.resize(0);
- if (edited_collision_shape.is_valid()) {
- for (int j = 0; j < edited_collision_shape->get_points().size(); j++) {
- current_shape.push_back(edited_collision_shape->get_points()[j] + shape_anchor);
+void TileSetEditor::select_coord(const Vector2 &coord) {
+ current_shape = PoolVector2Array();
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0))
+ edited_collision_shape = tileset->tile_get_shape(get_current_tile(), 0);
+ if (edited_occlusion_shape != tileset->tile_get_light_occluder(get_current_tile()))
+ edited_occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
+ if (edited_navigation_shape != tileset->tile_get_navigation_polygon(get_current_tile()))
+ edited_navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile());
+
+ if (edit_mode == EDITMODE_COLLISION) {
+ current_shape.resize(0);
+ if (edited_collision_shape.is_valid()) {
+ for (int i = 0; i < edited_collision_shape->get_points().size(); i++) {
+ current_shape.push_back(edited_collision_shape->get_points()[i]);
+ }
}
- }
- } else if (edit_mode == EDITMODE_OCCLUSION) {
- current_shape.resize(0);
- if (edited_occlusion_shape.is_valid()) {
- for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
- current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor);
+ } else if (edit_mode == EDITMODE_OCCLUSION) {
+ current_shape.resize(0);
+ if (edited_occlusion_shape.is_valid()) {
+ for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
+ current_shape.push_back(edited_occlusion_shape->get_polygon()[i]);
+ }
+ }
+ } else if (edit_mode == EDITMODE_NAVIGATION) {
+ current_shape.resize(0);
+ if (edited_navigation_shape.is_valid()) {
+ if (edited_navigation_shape->get_polygon_count() > 0) {
+ PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices();
+ for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
+ current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]]);
+ }
+ }
}
}
- } else if (edit_mode == EDITMODE_NAVIGATION) {
- current_shape.resize(0);
- if (edited_navigation_shape.is_valid()) {
- if (edited_navigation_shape->get_polygon_count() > 0) {
- PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices();
- for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
- current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor);
+ } else {
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->autotile_get_size(get_current_tile());
+ Vector2 shape_anchor = coord;
+ shape_anchor.x *= (size.x + spacing);
+ shape_anchor.y *= (size.y + spacing);
+ if (edit_mode == EDITMODE_COLLISION) {
+ current_shape.resize(0);
+ if (edited_collision_shape.is_valid()) {
+ for (int j = 0; j < edited_collision_shape->get_points().size(); j++) {
+ current_shape.push_back(edited_collision_shape->get_points()[j] + shape_anchor);
+ }
+ }
+ } else if (edit_mode == EDITMODE_OCCLUSION) {
+ current_shape.resize(0);
+ if (edited_occlusion_shape.is_valid()) {
+ for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
+ current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor);
+ }
+ }
+ } else if (edit_mode == EDITMODE_NAVIGATION) {
+ current_shape.resize(0);
+ if (edited_navigation_shape.is_valid()) {
+ if (edited_navigation_shape->get_polygon_count() > 0) {
+ PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices();
+ for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
+ current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor);
+ }
}
}
}
}
}
-Vector2 AutotileEditor::snap_point(const Vector2 &point) {
+Vector2 TileSetEditor::snap_point(const Vector2 &point) {
Vector2 p = point;
Vector2 coord = edited_shape_coord;
- Vector2 tile_size = tile_set->autotile_get_size(get_current_tile());
- int spacing = tile_set->autotile_get_spacing(get_current_tile());
+ Vector2 tile_size = tileset->autotile_get_size(get_current_tile());
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
Vector2 anchor = coord;
anchor.x *= (tile_size.x + spacing);
anchor.y *= (tile_size.y + spacing);
Rect2 region(anchor, tile_size);
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE)
+ region.position = Point2(0, 0);
+
if (tools[SHAPE_GRID_SNAP]->is_pressed()) {
p.x = Math::snap_scalar_seperation(snap_offset.x, snap_step.x, p.x, snap_separation.x);
p.y = Math::snap_scalar_seperation(snap_offset.y, snap_step.y, p.y, snap_separation.y);
@@ -1583,86 +1715,138 @@ Vector2 AutotileEditor::snap_point(const Vector2 &point) {
return p;
}
-void AutotileEditor::edit(Object *p_node) {
+void TileSetEditor::update_tile_list() {
+ int selected_tile = get_current_tile();
- tile_set = Ref<TileSet>(Object::cast_to<TileSet>(p_node));
- tile_set->add_change_receptor(this);
- helper->set_tileset(tile_set);
+ if (selected_tile < 0)
+ selected_tile = 0;
- autotile_list->clear();
+ helper->set_tileset(tileset);
+
+ tile_list->clear();
List<int> ids;
- tile_set->get_tile_list(&ids);
+ tileset->get_tile_list(&ids);
for (List<int>::Element *E = ids.front(); E; E = E->next()) {
- if (tile_set->tile_get_is_autotile(E->get())) {
- autotile_list->add_item(tile_set->tile_get_name(E->get()));
- autotile_list->set_item_metadata(autotile_list->get_item_count() - 1, E->get());
- autotile_list->set_item_icon(autotile_list->get_item_count() - 1, tile_set->tile_get_texture(E->get()));
- Rect2 region = tile_set->tile_get_region(E->get());
- region.size = tile_set->autotile_get_size(E->get());
- Vector2 pos = tile_set->autotile_get_icon_coordinate(E->get());
- pos.x *= (tile_set->autotile_get_spacing(E->get()) + region.size.x);
- pos.y *= (tile_set->autotile_get_spacing(E->get()) + region.size.y);
+ tile_list->add_item(tileset->tile_get_name(E->get()));
+ tile_list->set_item_metadata(tile_list->get_item_count() - 1, E->get());
+ tile_list->set_item_icon(tile_list->get_item_count() - 1, tileset->tile_get_texture(E->get()));
+ Rect2 region = tileset->tile_get_region(E->get());
+ if (tileset->tile_get_tile_mode(E->get()) == TileSet::AUTO_TILE) {
+ region.size = tileset->autotile_get_size(E->get());
+ Vector2 pos = tileset->autotile_get_icon_coordinate(E->get());
+ pos.x *= (tileset->autotile_get_spacing(E->get()) + region.size.x);
+ pos.y *= (tileset->autotile_get_spacing(E->get()) + region.size.y);
region.position += pos;
- autotile_list->set_item_icon_region(autotile_list->get_item_count() - 1, region);
}
+ tile_list->set_item_icon_region(tile_list->get_item_count() - 1, region);
+ tile_list->set_item_icon_modulate(tile_list->get_item_count() - 1, tileset->tile_get_modulate(E->get()));
}
- if (autotile_list->get_item_count() > 0) {
- autotile_list->select(0);
- _on_autotile_selected(0);
+ if (tile_list->get_item_count() > 0 && selected_tile < tile_list->get_item_count()) {
+ tile_list->select(selected_tile);
+ _on_tile_list_selected(selected_tile);
}
helper->_change_notify("");
}
-int AutotileEditor::get_current_tile() {
+void TileSetEditor::update_tile_list_icon() {
+ List<int> ids;
+ tileset->get_tile_list(&ids);
+ int current_idx = 0;
+ for (List<int>::Element *E = ids.front(); E; E = E->next()) {
+ if (current_idx >= tile_list->get_item_count())
+ break;
+
+ Rect2 region = tileset->tile_get_region(E->get());
+ if (tileset->tile_get_tile_mode(E->get()) == TileSet::AUTO_TILE) {
+ region.size = tileset->autotile_get_size(E->get());
+ Vector2 pos = tileset->autotile_get_icon_coordinate(E->get());
+ pos.x *= (tileset->autotile_get_spacing(E->get()) + region.size.x);
+ pos.y *= (tileset->autotile_get_spacing(E->get()) + region.size.y);
+ region.position += pos;
+ }
+ tile_list->set_item_metadata(current_idx, E->get());
+ tile_list->set_item_icon(current_idx, tileset->tile_get_texture(E->get()));
+ tile_list->set_item_icon_region(current_idx, region);
+ tile_list->set_item_icon_modulate(current_idx, tileset->tile_get_modulate(E->get()));
+ tile_list->set_item_text(current_idx, tileset->tile_get_name(E->get()));
+ current_idx += 1;
+ }
+ tile_list->update();
+}
- if (autotile_list->get_selected_items().size() == 0)
+void TileSetEditor::update_workspace_tile_mode() {
+ if (get_current_tile() < 0)
+ return;
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) {
+ tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
+ _on_edit_mode_changed(EDITMODE_COLLISION);
+ } else {
+ select_coord(Vector2(0, 0));
+ }
+
+ tool_editmode[EDITMODE_ICON]->hide();
+ tool_editmode[EDITMODE_BITMASK]->hide();
+ tool_editmode[EDITMODE_PRIORITY]->hide();
+ property_editor->hide();
+ } else {
+ tool_editmode[EDITMODE_ICON]->show();
+ tool_editmode[EDITMODE_BITMASK]->show();
+ tool_editmode[EDITMODE_PRIORITY]->show();
+ property_editor->show();
+ }
+}
+
+int TileSetEditor::get_current_tile() {
+ if (tile_list->get_selected_items().size() == 0)
return -1;
else
- return autotile_list->get_item_metadata(autotile_list->get_selected_items()[0]);
+ return tile_list->get_item_metadata(tile_list->get_selected_items()[0]);
}
-void AutotileEditorHelper::set_tileset(const Ref<TileSet> &p_tileset) {
+void TileSetEditorHelper::set_tileset(const Ref<TileSet> &p_tileset) {
- tile_set = p_tileset;
+ tileset = p_tileset;
}
-bool AutotileEditorHelper::_set(const StringName &p_name, const Variant &p_value) {
+bool TileSetEditorHelper::_set(const StringName &p_name, const Variant &p_value) {
- if (autotile_editor->get_current_tile() < 0 || tile_set.is_null())
+ if (selected_tile < 0 || tileset.is_null())
return false;
String name = p_name.operator String();
bool v = false;
if (name == "bitmask_mode") {
- tile_set->set(String::num(autotile_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v);
+ tileset->set(String::num(selected_tile, 0) + "/autotile/bitmask_mode", p_value, &v);
} else if (name.left(7) == "layout/") {
- tile_set->set(String::num(autotile_editor->get_current_tile(), 0) + "/autotile" + name.right(6), p_value, &v);
+ tileset->set(String::num(selected_tile, 0) + "/autotile" + name.right(6), p_value, &v);
}
if (v) {
- tile_set->_change_notify("");
- autotile_editor->workspace->update();
+ tileset->_change_notify("autotile");
}
return v;
}
-bool AutotileEditorHelper::_get(const StringName &p_name, Variant &r_ret) const {
+bool TileSetEditorHelper::_get(const StringName &p_name, Variant &r_ret) const {
- if (autotile_editor->get_current_tile() < 0 || tile_set.is_null())
+ if (selected_tile < 0 || tileset.is_null())
+ return false;
+ if (!tileset->has_tile(selected_tile))
return false;
String name = p_name.operator String();
bool v = false;
if (name == "bitmask_mode") {
- r_ret = tile_set->get(String::num(autotile_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", &v);
+ r_ret = tileset->get(String::num(selected_tile, 0) + "/autotile/bitmask_mode", &v);
} else if (name.left(7) == "layout/") {
- r_ret = tile_set->get(String::num(autotile_editor->get_current_tile(), 0) + "/autotile" + name.right(6), &v);
+ r_ret = tileset->get(String::num(selected_tile, 0) + "/autotile" + name.right(6), &v);
}
return v;
}
-void AutotileEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const {
+void TileSetEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const {
- if (autotile_editor->get_current_tile() < 0 || tile_set.is_null())
+ if (selected_tile < 0 || tileset.is_null())
return;
p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3"));
@@ -1670,7 +1854,72 @@ void AutotileEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const
p_list->push_back(PropertyInfo(Variant::INT, "layout/spacing", PROPERTY_HINT_RANGE, "0,256,1"));
}
-AutotileEditorHelper::AutotileEditorHelper(AutotileEditor *p_autotile_editor) {
+TileSetEditorHelper::TileSetEditorHelper(TileSetEditor *p_tileset_editor) {
+
+ tileset_editor = p_tileset_editor;
+ selected_tile = -1;
+}
+
+void TileSetEditorPlugin::edit(Object *p_node) {
+
+ if (Object::cast_to<TileSet>(p_node)) {
+ tileset_editor->edit(Object::cast_to<TileSet>(p_node));
+ tileset_editor->show();
+ tileset_editor->texture_region_editor->edit(p_node);
+ } else
+ tileset_editor->hide();
+}
+
+bool TileSetEditorPlugin::handles(Object *p_node) const {
+
+ return p_node->is_class("TileSet");
+}
+
+void TileSetEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ tileset_editor->show();
+ tileset_editor->menu->show();
+ tileset_editor_button->show();
+ tileset_editor->side_panel->show();
+ if (tileset_editor_button->is_pressed()) {
+ tileset_editor->bottom_panel->show();
+ }
+ texture_region_button->show();
+ if (texture_region_button->is_pressed())
+ tileset_editor->texture_region_editor->show();
+ } else {
+ tileset_editor->hide();
+ tileset_editor->menu->hide();
+ tileset_editor->side_panel->hide();
+ tileset_editor->bottom_panel->hide();
+ tileset_editor_button->hide();
+ texture_region_button->hide();
+ tileset_editor->texture_region_editor->hide();
+ }
+}
+
+TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) {
+
+ tileset_editor = memnew(TileSetEditor(p_node));
+
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, tileset_editor);
+ tileset_editor->set_anchors_and_margins_preset(Control::PRESET_TOP_WIDE);
+ tileset_editor->set_end(Point2(0, 22));
+ tileset_editor->hide();
+
+ tileset_editor->texture_region_editor = memnew(TextureRegionEditor(p_node));
+ texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), tileset_editor->texture_region_editor);
+ texture_region_button->set_tooltip(TTR("Texture Region Editor"));
+
+ tileset_editor->texture_region_editor->set_custom_minimum_size(Size2(0, 200));
+ tileset_editor->texture_region_editor->hide();
+ texture_region_button->hide();
- autotile_editor = p_autotile_editor;
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, tileset_editor->side_panel);
+ tileset_editor->side_panel->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ tileset_editor->side_panel->set_custom_minimum_size(Size2(200, 0));
+ tileset_editor->side_panel->hide();
+ tileset_editor_button = p_node->add_bottom_panel_item(TTR("Tile Set"), tileset_editor->bottom_panel);
+ tileset_editor_button->hide();
}
diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h
index 93d403deea..4894d641a3 100644
--- a/editor/plugins/tile_set_editor_plugin.h
+++ b/editor/plugins/tile_set_editor_plugin.h
@@ -33,35 +33,38 @@
#include "editor/editor_name_dialog.h"
#include "editor/editor_node.h"
+#include "editor/plugins/texture_region_editor_plugin.h"
#include "scene/2d/sprite.h"
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/tile_set.h"
-class AutotileEditorHelper;
-class AutotileEditor : public Control {
+class TileSetEditorHelper;
+
+class TileSetEditor : public Control {
friend class TileSetEditorPlugin;
- friend class AutotileEditorHelper;
- GDCLASS(AutotileEditor, Control);
+ friend class TextureRegionEditor;
+
+ GDCLASS(TileSetEditor, Control);
enum EditMode {
- EDITMODE_ICON,
- EDITMODE_BITMASK,
EDITMODE_COLLISION,
EDITMODE_OCCLUSION,
EDITMODE_NAVIGATION,
+ EDITMODE_BITMASK,
EDITMODE_PRIORITY,
+ EDITMODE_ICON,
EDITMODE_MAX
};
- enum AutotileToolbars {
+ enum TileSetToolbar {
TOOLBAR_DUMMY,
TOOLBAR_BITMASK,
TOOLBAR_SHAPE,
TOOLBAR_MAX
};
- enum AutotileTools {
+ enum TileSetTools {
TOOL_SELECT,
BITMASK_COPY,
BITMASK_PASTE,
@@ -78,17 +81,18 @@ class AutotileEditor : public Control {
TOOL_MAX
};
- Ref<TileSet> tile_set;
+ Ref<TileSet> tileset;
+
Ref<ConvexPolygonShape2D> edited_collision_shape;
Ref<OccluderPolygon2D> edited_occlusion_shape;
Ref<NavigationPolygon> edited_navigation_shape;
- EditorNode *editor;
-
int current_item_index;
Sprite *preview;
ScrollContainer *scroll;
Control *workspace_container;
+ bool draw_handles;
+ Control *workspace_overlay;
Control *workspace;
Button *tool_editmode[EDITMODE_MAX];
HBoxContainer *tool_containers[TOOLBAR_MAX];
@@ -114,21 +118,51 @@ class AutotileEditor : public Control {
PoolVector2Array current_shape;
Map<Vector2, uint16_t> bitmask_map_copy;
+ EditorNode *editor;
+ TextureRegionEditor *texture_region_editor;
+ Control *bottom_panel;
Control *side_panel;
- ItemList *autotile_list;
+ ItemList *tile_list;
PropertyEditor *property_editor;
- AutotileEditorHelper *helper;
+ TileSetEditorHelper *helper;
+
+ MenuButton *menu;
+ ConfirmationDialog *cd;
+ EditorNameDialog *nd;
+ AcceptDialog *err_dialog;
+
+ enum {
- AutotileEditor(EditorNode *p_editor);
+ MENU_OPTION_ADD_ITEM,
+ MENU_OPTION_REMOVE_ITEM,
+ MENU_OPTION_CREATE_FROM_SCENE,
+ MENU_OPTION_MERGE_FROM_SCENE
+ };
+
+ int option;
+ void _menu_cbk(int p_option);
+ void _menu_confirm();
+ void _name_dialog_confirm(const String &name);
+
+ static void _import_node(Node *p_node, Ref<TileSet> p_library);
+ static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge);
protected:
static void _bind_methods();
void _notification(int p_what);
virtual void _changed_callback(Object *p_changed, const char *p_prop);
+public:
+ void edit(const Ref<TileSet> &p_tileset);
+ static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge = true);
+
+ TileSetEditor(EditorNode *p_editor);
+ ~TileSetEditor();
+
private:
- void _on_autotile_selected(int p_index);
+ void _on_tile_list_selected(int p_index);
void _on_edit_mode_changed(int p_edit_mode);
+ void _on_workspace_overlay_draw();
void _on_workspace_draw();
void _on_workspace_input(const Ref<InputEvent> &p_ie);
void _on_tool_clicked(int p_tool);
@@ -141,24 +175,28 @@ private:
void _set_snap_sep_x(float p_val);
void _set_snap_sep_y(float p_val);
+ void initialize_bottom_editor();
void draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>());
void draw_grid_snap();
void draw_polygon_shapes();
void close_shape(const Vector2 &shape_anchor);
void select_coord(const Vector2 &coord);
Vector2 snap_point(const Vector2 &point);
+ void update_tile_list();
+ void update_tile_list_icon();
+ void update_workspace_tile_mode();
- void edit(Object *p_node);
int get_current_tile();
};
-class AutotileEditorHelper : public Object {
+class TileSetEditorHelper : public Object {
- friend class AutotileEditor;
- GDCLASS(AutotileEditorHelper, Object);
+ friend class TileSetEditor;
+ GDCLASS(TileSetEditorHelper, Object);
- Ref<TileSet> tile_set;
- AutotileEditor *autotile_editor;
+ Ref<TileSet> tileset;
+ TileSetEditor *tileset_editor;
+ int selected_tile;
public:
void set_tileset(const Ref<TileSet> &p_tileset);
@@ -168,46 +206,7 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- AutotileEditorHelper(AutotileEditor *p_autotile_editor);
-};
-
-class TileSetEditor : public Control {
-
- friend class TileSetEditorPlugin;
- GDCLASS(TileSetEditor, Control);
-
- Ref<TileSet> tileset;
-
- EditorNode *editor;
- MenuButton *menu;
- ConfirmationDialog *cd;
- EditorNameDialog *nd;
- AcceptDialog *err_dialog;
-
- enum {
-
- MENU_OPTION_ADD_ITEM,
- MENU_OPTION_REMOVE_ITEM,
- MENU_OPTION_CREATE_FROM_SCENE,
- MENU_OPTION_MERGE_FROM_SCENE
- };
-
- int option;
- void _menu_cbk(int p_option);
- void _menu_confirm();
- void _name_dialog_confirm(const String &name);
-
- static void _import_node(Node *p_node, Ref<TileSet> p_library);
- static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge);
-
-protected:
- static void _bind_methods();
-
-public:
- void edit(const Ref<TileSet> &p_tileset);
- static Error update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge = true);
-
- TileSetEditor(EditorNode *p_editor);
+ TileSetEditorHelper(TileSetEditor *p_tileset_editor);
};
class TileSetEditorPlugin : public EditorPlugin {
@@ -215,10 +214,10 @@ class TileSetEditorPlugin : public EditorPlugin {
GDCLASS(TileSetEditorPlugin, EditorPlugin);
TileSetEditor *tileset_editor;
- AutotileEditor *autotile_editor;
EditorNode *editor;
- ToolButton *autotile_button;
+ ToolButton *tileset_editor_button;
+ ToolButton *texture_region_button;
public:
virtual String get_name() const { return "TileSet"; }