diff options
93 files changed, 2608 insertions, 2402 deletions
diff --git a/SConstruct b/SConstruct index b8d3427690..14f576c595 100644 --- a/SConstruct +++ b/SConstruct @@ -145,7 +145,7 @@ opts = Variables(customs, ARGUMENTS) # Target build options opts.Add('arch', "Platform-dependent architecture (arm/arm64/x86/x64/mips/etc)", '') -opts.Add(EnumVariable('bits', "Target platform bits", 'default', ('default', '32', '64', 'fat'))) +opts.Add(EnumVariable('bits', "Target platform bits", 'default', ('default', '32', '64'))) opts.Add('p', "Platform (alias for 'platform')", '') opts.Add('platform', "Target platform (%s)" % ('|'.join(platform_list), ), '') opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'release_debug', 'release'))) @@ -382,8 +382,6 @@ if selected_platform in platform_list: suffix += ".32" elif (env["bits"] == "64"): suffix += ".64" - elif (env["bits"] == "fat"): - suffix += ".fat" suffix += env.extra_suffix diff --git a/core/array.cpp b/core/array.cpp index c53fea1f28..0ddac1662c 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -266,6 +266,20 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { return *this; } +void Array::shuffle() { + + const int n = _p->array.size(); + if (n < 2) + return; + Variant *data = _p->array.ptrw(); + for (int i = n - 1; i >= 1; i--) { + const int j = Math::rand() % (i + 1); + const Variant tmp = data[j]; + data[j] = data[i]; + data[i] = tmp; + } +} + template <typename Less> _FORCE_INLINE_ int bisect(const Vector<Variant> &p_array, const Variant &p_value, bool p_before, const Less &p_less) { diff --git a/core/array.h b/core/array.h index 7c6fc59048..684a8e265d 100644 --- a/core/array.h +++ b/core/array.h @@ -71,6 +71,7 @@ public: Array &sort(); Array &sort_custom(Object *p_obj, const StringName &p_function); + void shuffle(); int bsearch(const Variant &p_value, bool p_before = true); int bsearch_custom(const Variant &p_value, Object *p_obj, const StringName &p_function, bool p_before = true); Array &invert(); diff --git a/core/color.cpp b/core/color.cpp index a0568d26ed..27d8e9b891 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -400,6 +400,55 @@ String Color::to_html(bool p_alpha) const { return txt; } +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { + + const float h_ = p_h / 60.0f; + const float c = p_v * p_s; + const float x = c * (1.0f - Math::abs(Math::fmod(h_, 2.0f) - 1.0f)); + float r, g, b; + + switch ((int)h_) { + case 0: { + r = c; + g = x; + b = 0; + } break; + case 1: { + r = x; + g = c; + b = 0; + } break; + case 2: { + r = 0; + g = c; + b = x; + } break; + case 3: { + r = 0; + g = x; + b = c; + } break; + case 4: { + r = x; + g = 0; + b = c; + } break; + case 5: { + r = c; + g = 0; + b = x; + } break; + default: { + r = 0; + g = 0; + b = 0; + } break; + } + + const float m = p_v - c; + return Color(m + r, m + g, m + b, p_a); +} + float Color::gray() const { return (r + g + b) / 3.0; diff --git a/core/color.h b/core/color.h index 9af8fb0a78..a2015a34d6 100644 --- a/core/color.h +++ b/core/color.h @@ -190,6 +190,7 @@ struct Color { static bool html_is_valid(const String &p_color); static Color named(const String &p_name); String to_html(bool p_alpha = true) const; + Color from_hsv(float p_h, float p_s, float p_v, float p_a); _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys operator String() const; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 0991c0df68..b7fd6d7318 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -268,12 +268,12 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo if (FileAccessNetworkClient::get_singleton()) { - if (_load_settings("res://project.godot") == OK || _load_settings_binary("res://project.binary") == OK) { - - _load_settings("res://override.cfg"); + Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); + if (err == OK) { + // Optional, we don't mind if it fails + _load_settings_text("res://override.cfg"); } - - return OK; + return err; } String exec_path = OS::get_singleton()->get_executable_path(); @@ -285,12 +285,13 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo bool ok = _load_resource_pack(p_main_pack); ERR_FAIL_COND_V(!ok, ERR_CANT_OPEN); - if (_load_settings("res://project.godot") == OK || _load_settings_binary("res://project.binary") == OK) { - //load override from location of the main pack - _load_settings(p_main_pack.get_base_dir().plus_file("override.cfg")); + Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); + if (err == OK) { + // Load override from location of the main pack + // Optional, we don't mind if it fails + _load_settings_text(p_main_pack.get_base_dir().plus_file("override.cfg")); } - - return OK; + return err; } //Attempt with execname.pck @@ -313,12 +314,13 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo // if we opened our package, try and load our project... if (found) { - if (_load_settings("res://project.godot") == OK || _load_settings_binary("res://project.binary") == OK) { - // load override from location of executable - _load_settings(exec_path.get_base_dir().plus_file("override.cfg")); + Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); + if (err == OK) { + // Load override from location of executable + // Optional, we don't mind if it fails + _load_settings_text(exec_path.get_base_dir().plus_file("override.cfg")); } - - return OK; + return err; } } @@ -334,11 +336,13 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo // data.pck and data.zip are deprecated and no longer supported, apologies. // make sure this is loaded from the resource path - if (_load_settings("res://project.godot") == OK || _load_settings_binary("res://project.binary") == OK) { - _load_settings("res://override.cfg"); + Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); + if (err == OK) { + // Optional, we don't mind if it fails + _load_settings_text("res://override.cfg"); } - return OK; + return err; } //Nothing was found, try to find a project.godot somewhere! @@ -350,20 +354,23 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo String candidate = d->get_current_dir(); String current_dir = d->get_current_dir(); + bool found = false; + Error err; while (true) { - //try to load settings in ascending through dirs shape! - - if (_load_settings(current_dir + "/project.godot") == OK || _load_settings_binary(current_dir + "/project.binary") == OK) { - _load_settings(current_dir + "/override.cfg"); + err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary")); + if (err == OK) { + // Optional, we don't mind if it fails + _load_settings_text(current_dir.plus_file("override.cfg")); candidate = current_dir; found = true; break; } if (p_upwards) { + // Try to load settings ascending through dirs shape! d->change_dir(".."); if (d->get_current_dir() == current_dir) break; //not doing anything useful @@ -378,7 +385,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo memdelete(d); if (!found) - return ERR_FILE_NOT_FOUND; + return err; if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end @@ -440,7 +447,8 @@ Error ProjectSettings::_load_settings_binary(const String p_path) { return OK; } -Error ProjectSettings::_load_settings(const String p_path) { + +Error ProjectSettings::_load_settings_text(const String p_path) { Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -471,7 +479,7 @@ Error ProjectSettings::_load_settings(const String p_path) { memdelete(f); return OK; } else if (err != OK) { - ERR_PRINTS("ProjectSettings::load - " + p_path + ":" + itos(lines) + " error: " + error_text); + ERR_PRINTS("Error parsing " + p_path + " at line " + itos(lines) + ": " + error_text + " File might be corrupted."); memdelete(f); return err; } @@ -497,6 +505,22 @@ Error ProjectSettings::_load_settings(const String p_path) { return OK; } +Error ProjectSettings::_load_settings_text_or_binary(const String p_text_path, const String p_bin_path) { + + // Attempt first to load the text-based project.godot file + Error err_text = _load_settings_text(p_text_path); + if (err_text == OK) { + return OK; + } else if (err_text != ERR_FILE_NOT_FOUND) { + // If the text-based file exists but can't be loaded, we want to know it + return err_text; + } + + // Fallback to binary project.binary file if text-based was not found + Error err_bin = _load_settings_binary(p_bin_path); + return err_bin; +} + int ProjectSettings::get_order(const String &p_name) const { ERR_FAIL_COND_V(!props.has(p_name), -1); @@ -525,7 +549,7 @@ void ProjectSettings::clear(const String &p_name) { Error ProjectSettings::save() { - return save_custom(get_resource_path() + "/project.godot"); + return save_custom(get_resource_path().plus_file("project.godot")); } Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<String, List<String> > &props, const CustomMap &p_custom, const String &p_custom_features) { diff --git a/core/project_settings.h b/core/project_settings.h index eba53441cf..9b51bc3ac3 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -93,8 +93,9 @@ protected: static ProjectSettings *singleton; - Error _load_settings(const String p_path); + Error _load_settings_text(const String p_path); Error _load_settings_binary(const String p_path); + Error _load_settings_text_or_binary(const String p_text_path, const String p_bin_path); Error _save_settings_text(const String &p_file, const Map<String, List<String> > &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String()); Error _save_settings_binary(const String &p_file, const Map<String, List<String> > &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String()); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 5607751a27..d1d45f0e7c 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -443,6 +443,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Color, lightened); VCALL_LOCALMEM1R(Color, darkened); VCALL_LOCALMEM1R(Color, to_html); + VCALL_LOCALMEM4R(Color, from_hsv); VCALL_LOCALMEM0R(RID, get_id); @@ -490,6 +491,7 @@ struct _VariantCall { VCALL_LOCALMEM1(Array, erase); VCALL_LOCALMEM0(Array, sort); VCALL_LOCALMEM2(Array, sort_custom); + VCALL_LOCALMEM0(Array, shuffle); VCALL_LOCALMEM2R(Array, bsearch); VCALL_LOCALMEM4R(Array, bsearch_custom); VCALL_LOCALMEM0R(Array, duplicate); @@ -1589,6 +1591,7 @@ void register_variant_methods() { ADDFUNC1R(COLOR, COLOR, Color, lightened, REAL, "amount", varray()); ADDFUNC1R(COLOR, COLOR, Color, darkened, REAL, "amount", varray()); ADDFUNC1R(COLOR, STRING, Color, to_html, BOOL, "with_alpha", varray(true)); + ADDFUNC4R(COLOR, COLOR, Color, from_hsv, REAL, "h", REAL, "s", REAL, "v", REAL, "a", varray(1.0)); ADDFUNC0R(_RID, INT, RID, get_id, varray()); @@ -1634,6 +1637,7 @@ void register_variant_methods() { ADDFUNC0RNC(ARRAY, NIL, Array, pop_front, varray()); ADDFUNC0NC(ARRAY, NIL, Array, sort, varray()); ADDFUNC2NC(ARRAY, NIL, Array, sort_custom, OBJECT, "obj", STRING, "func", varray()); + ADDFUNC0NC(ARRAY, NIL, Array, shuffle, varray()); ADDFUNC2R(ARRAY, INT, Array, bsearch, NIL, "value", BOOL, "before", varray(true)); ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 9734a175b8..1e129d70d0 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -296,6 +296,11 @@ [/codeblock] </description> </method> + <method name="shuffle"> + <description> + Shuffle the array such that the items will have a random order. + </description> + </method> </methods> <constants> </constants> diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml index 3a1a7a4c90..a1aa479ea1 100644 --- a/doc/classes/RigidBody.xml +++ b/doc/classes/RigidBody.xml @@ -5,10 +5,9 @@ </brief_description> <description> This is the node that implements full 3D physics. This means that you do not control a RigidBody directly. Instead you can apply forces to it (gravity, impulses, etc.), and the physics simulation will calculate the resulting movement, collision, bouncing, rotating, etc. - This node can use custom force integration, for writing complex physics motion behavior per node. - This node can shift state between regular Rigid body, Kinematic, Character or Static. - Character mode forbids this node from being rotated. - As a warning, don't change RigidBody's position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop will yield strange behavior. + A RigidBody has 4 behavior [member mode]s: Rigid, Static, Character, and Kinematic. + [b]Note:[/b] Don't change a RigidBody's position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop will yield strange behavior. If you need to directly affect the body's state, use [method _integrate_forces], which allows you to directly access the physics state. + If you need to override the default physics behavior, you can write a custom force integration. See [member custom_integrator]. </description> <tutorials> http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html @@ -36,6 +35,15 @@ Apply a positioned impulse (which will be affected by the body mass and shape). This is the equivalent of hitting a billiard ball with a cue: a force that is applied once, and only once. Both the impulse and the position are in global coordinates, and the position is relative to the object's origin. </description> </method> + <method name="apply_torque_impulse"> + <return type="void"> + </return> + <argument index="1" name="impulse" type="Vector3"> + </argument> + <description> + Apply a torque impulse (which will be affected by the body mass and shape). This will rotate the body around the passed in vector. + </description> + </method> <method name="get_colliding_bodies" qualifiers="const"> <return type="Array"> </return> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 6a3302cf14..719ddfffe4 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -5,11 +5,7 @@ </brief_description> <description> This node implements simulated 2D physics. You do not control a RigidBody2D directly. Instead you apply forces to it (gravity, impulses, etc.) and the physics simulation calculates the resulting movement based on its mass, friction, and other physical properties. - A RigidBody2D has 4 behavior modes (see [member mode]): - - [b]Rigid[/b]: The body behaves as a physical object. It collides with other bodies and responds to forces applied to it. This is the default mode. - - [b]Static[/b]: The body behaves like a [StaticBody2D] and does not move. - - [b]Character[/b]: Similar to [code]Rigid[/code] mode, but the body can not rotate. - - [b]Kinematic[/b]: The body behaves like a [KinematicBody2D], and must be moved by code. + A RigidBody2D has 4 behavior [member mode]s: Rigid, Static, Character, and Kinematic. [b]Note:[/b] You should not change a RigidBody2D's [code]position[/code] or [code]linear_velocity[/code] every frame or even very often. If you need to directly affect the body's state, use [method _integrate_forces], which allows you to directly access the physics state. If you need to override the default physics behavior, you can write a custom force integration. See [member custom_integrator]. </description> diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 4a467293fd..3944431516 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -361,6 +361,7 @@ Error DirAccessUnix::rename(String p_path, String p_new_path) { return ::rename(p_path.utf8().get_data(), p_new_path.utf8().get_data()) == 0 ? OK : FAILED; } + Error DirAccessUnix::remove(String p_path) { if (p_path.is_rel_path()) diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 70334c2343..9ae9ab1501 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -666,6 +666,7 @@ void CodeTextEditor::_reset_zoom() { if (font.is_valid()) { EditorSettings::get_singleton()->set("interface/editor/code_font_size", 14); font->set_size(14); + zoom_nb->set_text("100%"); } } @@ -727,6 +728,9 @@ bool CodeTextEditor::_add_font_size(int p_delta) { if (font.is_valid()) { int new_size = CLAMP(font->get_size() + p_delta, 8 * EDSCALE, 96 * EDSCALE); + + zoom_nb->set_text(itos(100 * new_size / 14) + "%"); + if (new_size != font->get_size()) { EditorSettings::get_singleton()->set("interface/editor/code_font_size", new_size / EDSCALE); font->set_size(new_size); @@ -771,6 +775,14 @@ void CodeTextEditor::set_error(const String &p_error) { void CodeTextEditor::_update_font() { text_editor->add_font_override("font", get_font("source", "EditorFonts")); + + Ref<Font> status_bar_font = get_font("status_source", "EditorFonts"); + int count = status_bar->get_child_count(); + for (int i = 0; i < count; i++) { + Control *n = Object::cast_to<Control>(status_bar->get_child(i)); + if (n) + n->add_font_override("font", status_bar_font); + } } void CodeTextEditor::_on_settings_change() { @@ -851,7 +863,7 @@ CodeTextEditor::CodeTextEditor() { text_editor->set_brace_matching(true); text_editor->set_auto_indent(true); - HBoxContainer *status_bar = memnew(HBoxContainer); + status_bar = memnew(HBoxContainer); add_child(status_bar); status_bar->set_h_size_flags(SIZE_EXPAND_FILL); @@ -878,6 +890,24 @@ CodeTextEditor::CodeTextEditor() { status_bar->add_child(memnew(Label)); //to keep the height if the other labels are not visible + Label *zoom_txt = memnew(Label); + status_bar->add_child(zoom_txt); + zoom_txt->set_align(Label::ALIGN_RIGHT); + zoom_txt->set_valign(Label::VALIGN_CENTER); + zoom_txt->set_v_size_flags(SIZE_FILL); + zoom_txt->set_text(TTR("Zoom:")); + zoom_txt->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts")); + + zoom_nb = memnew(Label); + status_bar->add_child(zoom_nb); + zoom_nb->set_valign(Label::VALIGN_CENTER); + zoom_nb->set_v_size_flags(SIZE_FILL); + zoom_nb->set_autowrap(true); // workaround to prevent resizing the label on each change, do not touch + zoom_nb->set_clip_text(true); // workaround to prevent resizing the label on each change, do not touch + zoom_nb->set_custom_minimum_size(Size2(60, 1) * EDSCALE); + zoom_nb->set_align(Label::ALIGN_RIGHT); + zoom_nb->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts")); + Label *line_txt = memnew(Label); status_bar->add_child(line_txt); line_txt->set_align(Label::ALIGN_RIGHT); @@ -931,7 +961,8 @@ CodeTextEditor::CodeTextEditor() { code_complete_timer->connect("timeout", this, "_code_complete_timer_timeout"); font_resize_val = 0; - font_size = -1; + font_size = EditorSettings::get_singleton()->get("interface/editor/code_font_size"); + zoom_nb->set_text(itos(100 * font_size / 14) + "%"); font_resize_timer = memnew(Timer); add_child(font_resize_timer); font_resize_timer->set_one_shot(true); diff --git a/editor/code_editor.h b/editor/code_editor.h index db2e25b922..a860ad24e2 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -141,9 +141,11 @@ class CodeTextEditor : public VBoxContainer { TextEdit *text_editor; FindReplaceBar *find_replace_bar; + HBoxContainer *status_bar; Label *line_nb; Label *col_nb; + Label *zoom_nb; Label *info; Timer *idle; Timer *code_complete_timer; diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index d3e697ac35..0e9b7b74fd 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -92,45 +92,7 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin void EditorAutoloadSettings::_autoload_add() { - String name = autoload_add_name->get_text(); - - String error; - if (!_autoload_name_is_valid(name, &error)) { - EditorNode::get_singleton()->show_warning(error); - return; - } - - String path = autoload_add_path->get_line_edit()->get_text(); - if (!FileAccess::exists(path)) { - EditorNode::get_singleton()->show_warning(TTR("Invalid Path.") + "\n" + TTR("File does not exist.")); - return; - } - - if (!path.begins_with("res://")) { - EditorNode::get_singleton()->show_warning(TTR("Invalid Path.") + "\n" + TTR("Not in resource path.")); - return; - } - - name = "autoload/" + name; - - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - - undo_redo->create_action(TTR("Add AutoLoad")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), name, "*" + path); - - if (ProjectSettings::get_singleton()->has_setting(name)) { - undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, ProjectSettings::get_singleton()->get(name)); - } else { - undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, Variant()); - } - - undo_redo->add_do_method(this, "update_autoload"); - undo_redo->add_undo_method(this, "update_autoload"); - - undo_redo->add_do_method(this, "emit_signal", autoload_changed); - undo_redo->add_undo_method(this, "emit_signal", autoload_changed); - - undo_redo->commit_action(); + autoload_add(autoload_add_name->get_text(), autoload_add_path->get_line_edit()->get_text()); autoload_add_path->get_line_edit()->set_text(""); autoload_add_name->set_text(""); @@ -539,6 +501,74 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant & undo_redo->commit_action(); } +void EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_path) { + + String name = p_name; + + String error; + if (!_autoload_name_is_valid(name, &error)) { + EditorNode::get_singleton()->show_warning(error); + return; + } + + String path = p_path; + if (!FileAccess::exists(path)) { + EditorNode::get_singleton()->show_warning(TTR("Invalid Path.") + "\n" + TTR("File does not exist.")); + return; + } + + if (!path.begins_with("res://")) { + EditorNode::get_singleton()->show_warning(TTR("Invalid Path.") + "\n" + TTR("Not in resource path.")); + return; + } + + name = "autoload/" + name; + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(TTR("Add AutoLoad")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), name, "*" + path); + + if (ProjectSettings::get_singleton()->has_setting(name)) { + undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, ProjectSettings::get_singleton()->get(name)); + } else { + undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, Variant()); + } + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); +} + +void EditorAutoloadSettings::autoload_remove(const String &p_name) { + + String name = "autoload/" + p_name; + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + int order = ProjectSettings::get_singleton()->get_order(name); + + undo_redo->create_action(TTR("Remove Autoload")); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), name, Variant()); + + undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, ProjectSettings::get_singleton()->get(name)); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_persisting", name, true); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", order); + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); +} + void EditorAutoloadSettings::_bind_methods() { ClassDB::bind_method("_autoload_add", &EditorAutoloadSettings::_autoload_add); @@ -554,6 +584,8 @@ void EditorAutoloadSettings::_bind_methods() { ClassDB::bind_method("drop_data_fw", &EditorAutoloadSettings::drop_data_fw); ClassDB::bind_method("update_autoload", &EditorAutoloadSettings::update_autoload); + ClassDB::bind_method("autoload_add", &EditorAutoloadSettings::autoload_add); + ClassDB::bind_method("autoload_remove", &EditorAutoloadSettings::autoload_remove); ADD_SIGNAL(MethodInfo("autoload_changed")); } diff --git a/editor/editor_autoload_settings.h b/editor/editor_autoload_settings.h index 2e34a8b17f..6f622de6d5 100644 --- a/editor/editor_autoload_settings.h +++ b/editor/editor_autoload_settings.h @@ -87,6 +87,8 @@ protected: public: void update_autoload(); + void autoload_add(const String &p_name, const String &p_path); + void autoload_remove(const String &p_name); EditorAutoloadSettings(); }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 29912feb1f..6d94aedfa9 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1784,7 +1784,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { editor_data.save_editor_external_data(); } - if (!_call_build()) + if (!call_build()) return; if (bool(EDITOR_DEF("run/output/always_clear_output_on_play", true))) { @@ -2321,7 +2321,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if (run_native->is_deploy_debug_remote_enabled()) { _menu_option_confirm(RUN_STOP, true); - if (!_call_build()) + if (!call_build()) break; // build failed emit_signal("play_pressed"); @@ -4525,7 +4525,7 @@ void EditorNode::add_build_callback(EditorBuildCallback p_callback) { EditorBuildCallback EditorNode::build_callbacks[EditorNode::MAX_BUILD_CALLBACKS]; -bool EditorNode::_call_build() { +bool EditorNode::call_build() { for (int i = 0; i < build_callback_count; i++) { if (!build_callbacks[i]()) diff --git a/editor/editor_node.h b/editor/editor_node.h index 03102cdc81..6d96c2dea7 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -596,7 +596,6 @@ private: static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS]; void _save_default_environment(); - bool _call_build(); static int build_callback_count; static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS]; @@ -634,6 +633,8 @@ protected: static void _bind_methods(); public: + bool call_build(); + static void add_plugin_init_callback(EditorPluginInitializeCallback p_callback); enum EditorTable { @@ -653,6 +654,8 @@ public: PropertyEditor *get_property_editor() { return property_editor; } VBoxContainer *get_property_editor_vb() { return prop_editor_vb; } + ProjectSettingsEditor *get_project_settings() { return project_settings; } + static void add_editor_plugin(EditorPlugin *p_editor); static void remove_editor_plugin(EditorPlugin *p_editor); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 733680645f..31d0bfa8a9 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -301,6 +301,14 @@ void EditorPlugin::remove_custom_type(const String &p_type) { EditorNode::get_editor_data().remove_custom_type(p_type); } +void EditorPlugin::add_autoload_singleton(const String &p_name, const String &p_path) { + EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_add(p_name, p_path); +} + +void EditorPlugin::remove_autoload_singleton(const String &p_name) { + EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_remove(p_name); +} + ToolButton *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) { return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control); @@ -705,6 +713,9 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_custom_type", "type", "base", "script", "icon"), &EditorPlugin::add_custom_type); ClassDB::bind_method(D_METHOD("remove_custom_type", "type"), &EditorPlugin::remove_custom_type); + ClassDB::bind_method(D_METHOD("add_autoload_singleton", "name", "path"), &EditorPlugin::add_autoload_singleton); + ClassDB::bind_method(D_METHOD("remove_autoload_singleton", "name"), &EditorPlugin::remove_autoload_singleton); + ClassDB::bind_method(D_METHOD("update_overlays"), &EditorPlugin::update_overlays); ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index e3e405479c..05cc6e07e3 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -212,6 +212,9 @@ public: void add_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer); void remove_scene_import_plugin(const Ref<EditorSceneImporter> &p_importer); + void add_autoload_singleton(const String &p_name, const String &p_path); + void remove_autoload_singleton(const String &p_name); + EditorPlugin(); virtual ~EditorPlugin(); }; diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index c4ecf3c098..41b016bdca 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -402,19 +402,10 @@ void ExportTemplateManager::_http_download_templates_completed(int p_status, int if (p_code != 200) { template_list_state->set_text(TTR("Failed:") + " " + itos(p_code)); } else { - String path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_templates.tpz"); - FileAccess *f = FileAccess::open(path, FileAccess::WRITE); - if (!f) { - template_list_state->set_text(TTR("Can't write file.")); - } else { - int size = p_data.size(); - PoolVector<uint8_t>::Read r = p_data.read(); - f->store_buffer(r.ptr(), size); - memdelete(f); - template_list_state->set_text(TTR("Download Complete.")); - template_downloader->hide(); - _install_from_file(path, false); - } + String path = download_templates->get_download_file(); + template_list_state->set_text(TTR("Download Complete.")); + template_downloader->hide(); + _install_from_file(path, false); } } break; } @@ -437,6 +428,8 @@ void ExportTemplateManager::_begin_template_download(const String &p_url) { } download_data.clear(); + download_templates->set_download_file(EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_templates.tpz")); + download_templates->set_use_threads(true); Error err = download_templates->request(p_url); if (err != OK) { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e26f4ba449..0be3bb86c7 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -958,7 +958,12 @@ void FileSystemDock::_rename_operation_confirm() { //Present a more user friendly warning for name conflict DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); +#if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED) + // Workaround case insensitivity on Windows + if ((da->file_exists(new_path) || da->dir_exists(new_path)) && new_path.to_lower() != old_path.to_lower()) { +#else if (da->file_exists(new_path) || da->dir_exists(new_path)) { +#endif EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists.")); memdelete(da); return; diff --git a/editor/icons/SCsub b/editor/icons/SCsub index 9bd036dfa9..64992a9f90 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -34,7 +34,7 @@ def make_editor_icons_action(target, source, env): s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") s.write("#ifndef _EDITOR_ICONS_H\n") s.write("#define _EDITOR_ICONS_H\n") - s.write("static const int editor_icons_count = %s;\n" % len(svg_icons)) + s.write("static const int editor_icons_count = {};\n".format(len(svg_icons))) s.write("static const char *editor_icons_sources[] = {\n") s.write(icons_string.getvalue()) s.write('};\n\n') @@ -57,7 +57,7 @@ def make_editor_icons_action(target, source, env): if icon_name.endswith("BigThumb"): # don't know a better way to handle this thumb_big_indices.append(str(index)) - s.write('\t"%s"' % icon_name) + s.write('\t"{0}"'.format(icon_name)) if fname != svg_icons[-1]: s.write(",") @@ -69,13 +69,13 @@ def make_editor_icons_action(target, source, env): if thumb_medium_indices: s.write("\n\n") - s.write("static const int editor_md_thumbs_count = %s;\n" % len(thumb_medium_indices)) + s.write("static const int editor_md_thumbs_count = {};\n".format(len(thumb_medium_indices))) s.write("static const int editor_md_thumbs_indices[] = {") s.write(", ".join(thumb_medium_indices)) s.write("};\n") if thumb_big_indices: s.write("\n\n") - s.write("static const int editor_bg_thumbs_count = %s;\n" % len(thumb_big_indices)) + s.write("static const int editor_bg_thumbs_count = {};\n".format(len(thumb_big_indices))) s.write("static const int editor_bg_thumbs_indices[] = {") s.write(", ".join(thumb_big_indices)) s.write("};\n") diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index 78fc9ec9bd..21803a2184 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -188,7 +188,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati return OK; } -static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p_single_mesh, bool p_generate_tangents, List<String> *r_missing_deps) { +static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, List<String> *r_missing_deps) { FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); @@ -198,6 +198,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p mesh.instance(); bool generate_tangents = p_generate_tangents; + Vector3 scale_mesh = p_scale_mesh; bool flip_faces = false; //bool flip_faces = p_options["force/flip_faces"]; //bool force_smooth = p_options["force/smooth_shading"]; @@ -227,9 +228,9 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p Vector<String> v = l.split(" ", false); ERR_FAIL_COND_V(v.size() < 4, ERR_FILE_CORRUPT); Vector3 vtx; - vtx.x = v[1].to_float(); - vtx.y = v[2].to_float(); - vtx.z = v[3].to_float(); + vtx.x = v[1].to_float() * scale_mesh.x; + vtx.y = v[2].to_float() * scale_mesh.y; + vtx.z = v[3].to_float() * scale_mesh.z; vertices.push_back(vtx); } else if (l.begins_with("vt ")) { //uv @@ -401,7 +402,7 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, in List<Ref<Mesh> > meshes; - Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, r_missing_deps); + Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), r_missing_deps); if (err != OK) { if (r_err) { @@ -468,6 +469,7 @@ String ResourceImporterOBJ::get_preset_name(int p_idx) const { void ResourceImporterOBJ::get_import_options(List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1))); } bool ResourceImporterOBJ::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { @@ -478,7 +480,7 @@ Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_s List<Ref<Mesh> > meshes; - Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], NULL); + Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["scale_mesh"], NULL); ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 4a05d401cb..9680ad07e7 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -169,64 +169,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; } @@ -263,7 +209,6 @@ 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 }; // Smart snap using the canvas position @@ -271,12 +216,12 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const real_t rotation = 0.0; if (p_canvas_item) { - Point2 begin; - Point2 end; rotation = p_canvas_item->get_global_transform_with_canvas().get_rotation(); + // Parent sides and center if ((snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { - // Parent sides and center + Point2 begin; + Point2 end; bool can_snap = false; 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))); @@ -295,23 +240,29 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const } } - // Self anchors (for sides) + // Self anchors 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)))); + 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, end, output, snapped, rotation); } } - // Self sides (for anchors) + // Self sides 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()); + 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 center + if ((snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) { + 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); + } } // Other nodes sides @@ -340,11 +291,11 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const // 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,7 +313,6 @@ 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; } @@ -377,48 +327,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 +350,109 @@ 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"]; - } +Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_list) { + ERR_FAIL_COND_V(p_list.empty(), Rect2()); - 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"]; - } + // Handles the first element + CanvasItem *canvas_item = p_list.front()->get(); + Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2()); - 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); - } - - 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_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); + // Handles the other ones + for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) { + CanvasItem *canvas_item = E->get(); + Rect2 current_rect = canvas_item->_edit_get_rect(); + Transform2D xform = canvas_item->get_global_transform_with_canvas(); + 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))); } - 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); - } + return rect; +} - 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); - } +void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, 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("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); - } + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); - 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); - } + 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_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 (!lock_children && (!inherited || editable)) { + 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 { + 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_")) { + Rect2 rect = canvas_item->_edit_get_rect(); + Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); + if (r_first) { + r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2()); + r_first = false; + } + 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)); } - - 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(); - - return item_owner && item_owner != scene_node && p_item != scene_node && item_owner->get_filename() != ""; +Rect2 CanvasItemEditor::_get_scene_encompassing_rect() { + Rect2 rect; + bool first = true; + _expand_encompassing_rect_using_children(rect, editor->get_edited_scene(), first); + 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 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); + if (canvas_item && !canvas_item->is_set_as_toplevel()) + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, 0, 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, 0, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); //use base transform } - if (limit != 0 && r_items.size() >= limit) return; } - if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) { + if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) { - 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); + 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 (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->_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 +462,45 @@ 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::_find_canvas_items_at_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); 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 = 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 (!lock_children && (!inherited || editable)) { for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - - 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 (canvas_item && !canvas_item->is_set_as_toplevel()) { + _find_canvas_items_at_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_at_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)) { + if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) { - Rect2 rect = c->_edit_get_rect(); - Transform2D xform = p_parent_xform * p_canvas_xform * c->get_transform(); + Rect2 rect = canvas_item->_edit_get_rect(); + 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)))) { - r_items->push_back(c); + 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 +509,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 +522,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 +567,90 @@ 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, - }; +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(); + se->pre_drag_rect = canvas_item->_edit_get_rect(); + + 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; + 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()); + } - 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; - } else { - return dragger[i]; + //Save the bone state and lenght 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 +662,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,124 +672,16 @@ 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 draging 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 + // Retreive the guide lists Array vguides; if (EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); @@ -1251,48 +694,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 (b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { - // Release button + 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; + } + + // 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 +766,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,96 +836,22 @@ 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(); - } - } - } - - 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"); - } - 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(); - } - } - - 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 (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(); - viewport->update(); - } else - return; - - accept_event(); + drag_type = DRAG_NONE; + viewport->update(); + return true; } } + return false; } -void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { - - { - EditorNode *en = editor; - EditorPluginList *over_plugin_list = en->get_editor_plugins_over(); - - if (!over_plugin_list->empty()) { - bool discard = over_plugin_list->forward_gui_input(p_event); - if (discard) { - accept_event(); - return; - } - } - } - - Ref<InputEventMagnifyGesture> magnify_gesture = p_event; - if (magnify_gesture.is_valid()) { - - _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - return; - } - - Ref<InputEventPanGesture> pan_gesture = p_event; - if (pan_gesture.is_valid()) { - - 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; - } - +bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - // Button event - if (b->get_button_index() == BUTTON_WHEEL_DOWN) { // Scroll or pan down if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { @@ -1476,8 +861,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { } else { _zoom_on_position(zoom * (1 - (0.05 * b->get_factor())), b->get_position()); } - - return; + return true; } if (b->get_button_index() == BUTTON_WHEEL_UP) { @@ -1489,768 +873,988 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { } else { _zoom_on_position(zoom * ((0.95 + (0.05 * b->get_factor())) / 0.95), b->get_position()); } - - return; + return true; } if (b->get_button_index() == BUTTON_WHEEL_LEFT) { // Pan left if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { - h_scroll->set_value(h_scroll->get_value() - int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor()); + return true; } } if (b->get_button_index() == BUTTON_WHEEL_RIGHT) { // Pan right if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { - h_scroll->set_value(h_scroll->get_value() + int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor()); + return true; } } + } - if (b->get_button_index() == BUTTON_RIGHT) { + Ref<InputEventMouseMotion> m = p_event; + if (m.is_valid()) { + if (drag_type == DRAG_NONE) { + if (((m->get_button_mask() & BUTTON_MASK_LEFT) && tool == TOOL_PAN) || + (m->get_button_mask() & BUTTON_MASK_MIDDLE) || + ((m->get_button_mask() & BUTTON_MASK_LEFT) && Input::get_singleton()->is_key_pressed(KEY_SPACE)) || + (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + // 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(); + } - if (b->is_pressed() && (tool == TOOL_SELECT && b->get_alt())) { - // Open the selection list - _list_select(b); - return; + h_scroll->set_value(h_scroll->get_value() - relative.x / zoom); + v_scroll->set_value(v_scroll->get_value() - relative.y / zoom); + return true; } + } + } - 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); - } + 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 true; + } - bone_ik_list.clear(); + 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 true; + } - } 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); - } - } + return false; +} - drag = DRAG_NONE; - viewport->update(); - can_move_pivot = false; +bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseMotion> m = p_event; + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventKey> k = p_event; - } else if (box_selecting) { - // Cancel box selection - box_selecting = false; - viewport->update(); + // 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; - } - if (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT) { - if (b->is_pressed()) - // Open the selection list - _list_select(b); - 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_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); + drag_type = DRAG_PIVOT; + _save_canvas_item_state(drag_selection); } - return; + return true; } + } - 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 (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->is_pressed()) { + // 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; + } - if (drag != DRAG_NONE) { - // Stop dragging - if (undo_redo) { + // 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 (bone_ik_list.size()) { - undo_redo->create_action(TTR("Edit IK Chain")); +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(); - for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) { + // 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++; + } + } - 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); - } + // 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; + } - undo_redo->add_do_method(viewport, "update"); - undo_redo->add_undo_method(viewport, "update"); + 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; - 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); - } - } - } - undo_redo->commit_action(); - } + // 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 { + drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin(); } + _save_canvas_item_state(drag_selection); + return true; + } + } + } - drag = DRAG_NONE; + 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(); - can_move_pivot = false; } + return true; + } - if (box_selecting) { - // Stop box selection - Node *scene = editor->get_edited_scene(); - if (scene) { - - List<CanvasItem *> selitems; - - 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); - - _find_canvas_items_at_rect(Rect2(bsfrom, bsto - bsfrom), scene, transform, Transform2D(), &selitems); + // 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; + } - for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) { + // 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; +} - _append_canvas_item(E->get()); - } - } +bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; - box_selecting = false; - viewport->update(); + // 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; } + } + return false; +} - // -- From now we consider that the button is BUTTON_LEFT and that it is pressed -- +bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; - Map<ObjectID, BoneList>::Element *Cbone = NULL; //closest + // 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)); - { - 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()) { + 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); + } - if (E->get().from == E->get().to) - continue; - Vector2 s[2] = { - E->get().from, - E->get().to - }; + DragType dragger[] = { + DRAG_ANCHOR_TOP_LEFT, + DRAG_ANCHOR_TOP_RIGHT, + DRAG_ANCHOR_BOTTOM_RIGHT, + DRAG_ANCHOR_BOTTOM_LEFT, + }; - 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; + 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; + } + } } } + } + } - if (Cbone) { - Node2D *b = Object::cast_to<Node2D>(ObjectDB::get_instance(Cbone->get().bone)); + 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]); - if (b) { + drag_to = transform.affine_inverse().xform(m->get_position()); - bool ik_found = false; + Transform2D xform = control->get_global_transform_with_canvas().affine_inverse(); - bool first = true; + 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)); - while (b) { + 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)); - CanvasItem *pi = b->get_parent_item(); - if (!pi) - break; + 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); - float len = pi->get_global_transform().get_origin().distance_to(b->get_global_position()); - b = Object::cast_to<Node2D>(pi); - if (!b) - break; + switch (drag_type) { + case DRAG_ANCHOR_TOP_LEFT: + if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); + if (!use_single_axis || (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_single_axis && !use_y)) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); + if (!use_single_axis || (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_single_axis && !use_y)) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, false); + if (!use_single_axis || (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_single_axis && !use_y)) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, false); + if (!use_single_axis || (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_single_axis && !use_y)) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); + if (!use_single_axis || (use_single_axis && !use_y)) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); + if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); + if (!use_single_axis || (use_single_axis && use_y)) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); + break; + default: + break; + } + return true; + } - if (first) { + // 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; + } - bone_orig_xform = b->get_global_transform(); - first = false; - } + // 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; +} - BoneIK bik; - bik.node = b; - bik.len = len; - bik.orig_state = b->_edit_get_state(); +bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> b = p_event; + Ref<InputEventMouseMotion> m = p_event; - bone_ik_list.push_back(bik); + // 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]; + + 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)) + }; - if (b->has_meta("_edit_ik_")) { + DragType dragger[] = { + DRAG_TOP_LEFT, + DRAG_TOP, + DRAG_TOP_RIGHT, + DRAG_RIGHT, + DRAG_BOTTOM_RIGHT, + DRAG_BOTTOM, + DRAG_BOTTOM_LEFT, + DRAG_LEFT + }; - ik_found = bone_ik_list.size() > 1; - break; - } + DragType resize_drag = DRAG_NONE; + float radius = (select_handle->get_size().width / 2) * 1.5; - if (!pi->has_meta("_edit_bone_")) - break; - } + for (int i = 0; i < 4; i++) { + int prev = (i + 3) % 4; + int next = (i + 1) % 4; - if (!ik_found) - bone_ik_list.clear(); + 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]; + } + + 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; } } } + } - // Single selected item - CanvasItem *canvas_item = _get_single_item(); - if (canvas_item) { + 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); - 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; + //Reset state + canvas_item->_edit_set_state(se->undo_state); + + bool uniform = m->get_shift(); + bool symmetric = m->get_alt(); + + 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 (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; + // 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); + } + + // 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); + } + } 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; + } } } + } - // 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(); - - if ((b->get_alt() || tool == TOOL_MOVE) && get_item_count()) { - // Drag the nodes - _prepare_drag(click); + // 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; + return true; } - CanvasItem *c = NULL; - if (Cbone) { - c = Object::cast_to<CanvasItem>(ObjectDB::get_instance(Cbone->get().bone)); - if (c) - c = c->get_parent_item(); + // 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; +} - 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; +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 *cn = c; - while (cn) { - if (cn->has_meta("_edit_group_")) { - c = cn; - } - cn = cn->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 *n = c; - while ((n && n != scene && n->get_owner() != scene) || (n && !n->is_class("CanvasItem"))) { - n = n->get_parent(); - }; + if (drag_type == DRAG_ALL) { + // Move the nodes + if (m.is_valid()) { + _restore_canvas_item_state(drag_selection, true); - if (n) { - c = Object::cast_to<CanvasItem>(n); - } else { - c = NULL; - } + 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; + } + } - // 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; + 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(); + + 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)); + } + } + return true; } - } - Ref<InputEventMouseMotion> m = p_event; - if (m.is_valid()) { - // Mouse motion event - _update_cursor(); + // 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 (box_selecting) { - // Update box selection - box_selecting_to = transform.affine_inverse().xform(m->get_position()); + drag_type = DRAG_NONE; viewport->update(); - return; + return 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(); + // 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; + } + } - // 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; + // 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 (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()); - } else { - relative = m->get_relative(); - } + _restore_canvas_item_state(drag_selection, true); + + 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; + } - h_scroll->set_value(h_scroll->get_value() - relative.x / zoom); - v_scroll->set_value(v_scroll->get_value() - relative.y / zoom); + 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 { + new_pos = previous_pos + (drag_to - drag_from); } - - return; + } else { + new_pos = previous_pos + (drag_to - drag_from); } - 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; - + 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; - - bool dragging_bone = drag == DRAG_ALL && selection.size() == 1 && bone_ik_list.size(); + Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform(); - 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); + 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; + } - Vector2 dfrom = drag_from; - Vector2 dto = transform.affine_inverse().xform(m->get_position()); - if (canvas_item->has_meta("_edit_lock_")) - continue; + 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; + } - 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(); - } - } + 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; +} - { - Control *node = Object::cast_to<Control>(canvas_item); +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; - if (node) { - real_t angle = node->get_rotation(); - display_rotate_to = dto; - display_rotate_from = center + node->get_pivot_offset().rotated(angle); - node->set_rotation(snap_angle(angle + (dfrom - display_rotate_from).angle_to(display_rotate_to - display_rotate_from), angle)); - viewport->update(); - } + if (drag_type == DRAG_NONE) { + if (b.is_valid() && + ((b->get_button_index() == BUTTON_LEFT && b->get_alt() && tool == TOOL_SELECT) || + (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT))) { + // Popup the selection menu list + Point2 click = transform.xform(b->get_position()); + + Node *scene = editor->get_edited_scene(); + + _find_canvas_items_at_pos(click, scene, 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--; } - - continue; } - bool uniform = m->get_shift(); - bool symmetric = m->get_alt(); + if (selection_results.size() == 1) { + CanvasItem *item = selection_results[0].item; + selection_results.clear(); - 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; - } + _select_click_on_item(item, click, b->get_shift()); - 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(); + return true; + } else if (!selection_results.empty()) { + selection_results.sort(); - Vector2 anchor = c_trans_rev.xform(dto - drag_from + drag_point_from); - anchor = _position_to_anchor(control, anchor); + NodePath root_path = get_tree()->get_edited_scene_root()->get_path(); + StringName root_name = root_path.get_name(root_path.get_name_count() - 1); - 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)); + for (int i = 0; i < selection_results.size(); i++) { + CanvasItem *item = selection_results[i].item; - bool use_y = Math::abs(drag_vector.y) > Math::abs(drag_vector.x); + 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()); - 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; + 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); } - } - - 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); - - 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 (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; - } - } 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; - } + selection_menu_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 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; + 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()); - case DRAG_PIVOT: + Node *scene = editor->get_edited_scene(); + if (!scene) + return true; - 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: + // Find the item to select + CanvasItem *canvas_item = NULL; + Vector<_SelectResult> selection; + _find_canvas_items_at_pos(click, scene, selection, 1); + if (!selection.empty()) + canvas_item = selection[0].item; - ERR_FAIL_COND(!Object::cast_to<Node2D>(canvas_item)); - Object::cast_to<Node2D>(canvas_item)->set_global_position(dto); - continue; - break; + // Check if the canvas item is in a group, and select the group instead if it is the case + CanvasItem *canvas_item_tmp = canvas_item; + while (canvas_item_tmp) { + if (canvas_item->has_meta("_edit_group_")) { + canvas_item = canvas_item_tmp; + } + canvas_item_tmp = canvas_item_tmp->get_parent_item(); } - if (!dragging_bone) { + // Make sure the selected node is in the current scene + Node *node = canvas_item; + while (node && ((node != scene && node->get_owner() != scene) || !node->is_class("CanvasItem"))) { + node = node->get_parent(); + }; + canvas_item = Object::cast_to<CanvasItem>(node); - local_rect.position = begin; - local_rect.size = end - begin; - canvas_item->_edit_set_rect(local_rect); + if (!canvas_item) { + // Start a box selection + if (!b->get_shift()) { + // Clear the selection if not additive + editor_selection->clear(); + viewport->update(); + }; + drag_from = click; + drag_type = DRAG_BOX_SELECTION; + box_selecting_to = drag_from; + return true; } 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; - - if (n2d) { - - 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(); - } - - { - - final_xform.elements[2] += dto - dfrom; //final_xform.affine_inverse().basis_xform_inv(drag_vector); - //n2d->set_global_transform(final_xform); - } - - CanvasItem *last = bone_ik_list.back()->get().node; - if (!last) - 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"); + 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; + } + } + } - 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); + 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; - } 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 { + 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); - 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"); - } - } - } - } + _find_canvas_items_at_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems); + for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) { + editor_selection->add_node(E->get()); } + } - for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) { + drag_type = DRAG_NONE; + viewport->update(); + return true; + } - Node2D *n = E->get().node; + if (b.is_valid() && b->is_pressed() && b->get_button_index() == BUTTON_RIGHT) { + // Cancel box selection + drag_type = DRAG_NONE; + viewport->update(); + return true; + } - if (!E->prev()) { - //last goes to what it was - final_xform.set_origin(n->get_global_position()); - n->set_global_transform(final_xform); + if (m.is_valid()) { + // Update box selection + box_selecting_to = transform.affine_inverse().xform(m->get_position()); + viewport->update(); + return true; + } + } - } 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; - } + 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; +} - n->rotate(rot); - } +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_select(p_event))) { + //printf("Selection\n"); + } else if ((accepted = _gui_input_zoom_or_pan(p_event))) { + //printf("Zoom or pan\n"); + } + + if (accepted) + accept_event(); + + // Change the cursor + CursorShape c = CURSOR_ARROW; + switch (drag_type) { + 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; + default: + break; } - - 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; + 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 +1896,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 +1910,61 @@ 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"); // 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 || (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 +1975,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 +1988,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 +2035,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,26 +2070,23 @@ 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; 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); @@ -2507,8 +2106,8 @@ void CanvasItemEditor::_draw_selection() { Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform); + // Draw the selected items surrounding boxes Vector2 endpoints[4] = { - xform.xform(rect.position), xform.xform(rect.position + Vector2(rect.size.x, 0)), xform.xform(rect.position + rect.size), @@ -2524,25 +2123,13 @@ void CanvasItemEditor::_draw_selection() { } 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 +2148,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 +2164,8 @@ void CanvasItemEditor::_draw_selection() { case DRAG_ANCHOR_BOTTOM_LEFT: dragged_anchor = 3; break; + default: + break; } if (dragged_anchor >= 0) { @@ -2639,56 +2227,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 +2301,15 @@ void CanvasItemEditor::_draw_selection() { viewport->draw_rect(parent_transform.xform(rect), color_base, false); } break; + default: + break; } } } if (tool == TOOL_SELECT) { - for (int i = 0; i < 4; i++) { - + // Draw the resize handles int prev = (i + 3) % 4; int next = (i + 1) % 4; @@ -2728,9 +2326,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,8 +2336,50 @@ 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_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); } } @@ -2750,9 +2390,8 @@ void CanvasItemEditor::_draw_axis() { Color y_axis_color(0.4, 1.0, 0.4, 0.6); Color area_axis_color(0.4, 0.4, 1.0, 0.4); - 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); + _draw_straight_line(Point2(), Point2(1, 0), x_axis_color); + _draw_straight_line(Point2(), Point2(0, 1), y_axis_color); Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); @@ -2783,7 +2422,7 @@ void CanvasItemEditor::_draw_bones() { E->get().from = Vector2(); E->get().to = Vector2(); - Node2D *n2d = Object::cast_to<Node2D>(ObjectDB::get_instance(E->get().bone)); + Node2D *n2d = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key())); if (!n2d) continue; @@ -2881,7 +2520,6 @@ void CanvasItemEditor::_build_bones_list(Node *p_node) { ObjectID id = c->get_instance_id(); if (!bone_list.has(id)) { BoneList bone; - bone.bone = id; bone_list[id] = bone; } @@ -2890,32 +2528,6 @@ void CanvasItemEditor::_build_bones_list(Node *p_node) { } } -void CanvasItemEditor::_get_encompassing_rect(Node *p_node, Rect2 &r_rect, const Transform2D &p_xform) { - ERR_FAIL_COND(!p_node); - - for (int i = 0; i < p_node->get_child_count(); i++) { - _get_encompassing_rect(p_node->get_child(i), r_rect, p_xform); - } - - 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)); - } -} - -void CanvasItemEditor::_draw_viewport_base() { - if (show_rulers) - _draw_rulers(); - if (show_guides) - _draw_guides(); - _draw_focus(); -} - void CanvasItemEditor::_draw_viewport() { // hide/show buttons depending on the selection @@ -2968,36 +2580,25 @@ void CanvasItemEditor::_draw_viewport() { } _draw_bones(); + if (show_rulers) + _draw_rulers(); + if (show_guides) + _draw_guides(); + _draw_focus(); } 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; + 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(); Transform2D xform = canvas_item->get_transform(); @@ -3008,36 +2609,41 @@ void CanvasItemEditor::_notification(int p_what) { 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++; } } + // Activate / Deactivate the pivot tool + pivot_button->set_disabled(nb_having_pivot == 0); - if (all_control && has_control) - presets_menu->show(); - else - presets_menu->hide(); + // Show / Hide the layout button + presets_menu->set_visible(nb_control > 0 && nb_control == selection.size()); for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { - Object *b = ObjectDB::get_instance(E->get().bone); + Object *b = ObjectDB::get_instance(E->key()); if (!b) { viewport->update(); @@ -3150,63 +2756,60 @@ 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()); + 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); 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_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(0, size.height - hmin.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)); 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)); - Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); - 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()) { - if (E->get().last_pass != bone_last_frame) { bone_to_erase.push_back(E); } } - while (bone_to_erase.size()) { bone_list.erase(bone_to_erase.front()->get()); bone_to_erase.pop_front(); } - //expand area so it's easier to do animations and stuff at 0,0 + // Calculate scrollable area + Rect2 canvas_item_rect = Rect2(Point2(), screen_rect); + if (editor->get_edited_scene()) { + Rect2 content_rect = _get_scene_encompassing_rect(); + 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; @@ -3273,48 +2876,42 @@ void CanvasItemEditor::_update_scroll(float) { editor->get_scene_root()->set_global_canvas_transform(transform); 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 +2923,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(); @@ -3351,25 +2946,34 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { _update_scroll(0); viewport->update(); - viewport_base->update(); } -void CanvasItemEditor::_zoom_minus() { +void CanvasItemEditor::_button_zoom_minus() { _zoom_on_position(zoom / 2.0, 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() { +void CanvasItemEditor::_button_zoom_plus() { _zoom_on_position(zoom * 2.0, 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 +2986,6 @@ 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 SNAP_USE_NODE_PARENT: { snap_node_parent = !snap_node_parent; @@ -3399,6 +3002,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 +3032,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 +3059,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 +3081,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 +3109,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 +3306,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 +3384,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()); + */ } } @@ -3974,20 +3535,18 @@ 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("_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(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); @@ -3997,6 +3556,162 @@ void CanvasItemEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("item_group_status_changed")); } +Dictionary CanvasItemEditor::get_state() const { + + Dictionary state; + state["zoom"] = zoom; + state["ofs"] = Point2(h_scroll->get_value(), v_scroll->get_value()); + 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_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")) { + _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_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_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); @@ -4042,21 +3757,12 @@ 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); ViewportContainer *scene_tree = memnew(ViewportContainer); viewport_scrollable->add_child(scene_tree); @@ -4069,6 +3775,7 @@ 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"); @@ -4082,33 +3789,32 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { v_scroll->connect("value_changed", this, "_update_scroll", Vector<Variant>(), Object::CONNECT_DEFERRED); 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_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_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_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")); @@ -4116,14 +3822,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")); @@ -4132,19 +3838,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)); @@ -4152,7 +3858,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)); @@ -4179,6 +3885,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); @@ -4310,9 +4017,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); @@ -4320,9 +4024,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_rot = true; key_scale = false; - edited_guide_pos = Point2(); - edited_guide_index = -1; - show_grid = false; show_helpers = false; show_rulers = true; @@ -4337,6 +4038,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; @@ -4344,17 +4046,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()); } @@ -4377,7 +4081,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 ee9be86cce..adf203cf3d 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, @@ -169,6 +172,7 @@ class CanvasItemEditor : public VBoxContainer { enum DragType { DRAG_NONE, + DRAG_BOX_SELECTION, DRAG_LEFT, DRAG_TOP_LEFT, DRAG_TOP, @@ -185,29 +189,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 }; 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; @@ -233,6 +228,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 +236,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 { @@ -267,33 +259,17 @@ class CanvasItemEditor : public VBoxContainer { Transform2D xform; Vector2 from; Vector2 to; - ObjectID bone; 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; - }; - - List<BoneIK> bone_ik_list; - struct PoseClipboard { - Vector2 pos; Vector2 scale; float rot; ObjectID id; }; - List<PoseClipboard> pose_clipboard; ToolButton *select_button; @@ -333,16 +309,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 +330,19 @@ 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 _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_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _find_canvas_items_at_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 +351,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; - - 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, Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + Rect2 _get_scene_encompassing_rect(); 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 +373,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(); @@ -422,13 +385,23 @@ class CanvasItemEditor : public VBoxContainer { void _draw_locks_and_groups(Node *p_node, const Transform2D &p_xform); 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); 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,12 +410,13 @@ 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; @@ -494,9 +468,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); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index ce71cf3284..c06531d975 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1578,6 +1578,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) { @@ -2928,7 +2931,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 2e7652a7be..bcc604d990 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -165,6 +165,7 @@ class ScriptEditor : public PanelContainer { enum ScriptSortBy { SORT_BY_NAME, SORT_BY_PATH, + SORT_BY_NONE }; enum ScriptListName { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 87e92f0807..aeba0ff930 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -935,13 +935,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: { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 63762651d7..6fd93c2f28 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -4282,67 +4282,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 +4317,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 +4585,71 @@ void SpatialEditor::_init_indicators() { _generate_selection_box(); } +void SpatialEditor::_init_grid() { + + PoolVector<Color> grid_colors[3]; + PoolVector<Vector3> grid_points[3]; + + 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; + +#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()); + + 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]); @@ -4770,6 +4787,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(); } } diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 55866cac99..d1fcdf8d54 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -531,7 +531,9 @@ private: void _instance_scene(); void _init_indicators(); + void _init_grid(); void _finish_indicators(); + void _finish_grid(); void _toggle_maximize_view(Object *p_viewport); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index da283b16dc..bd700342f6 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -304,8 +304,9 @@ private: ProjectSettings *current = memnew(ProjectSettings); - if (current->setup(dir, "")) { - set_message(TTR("Couldn't get project.godot in project path."), MESSAGE_ERROR); + int err = current->setup(dir, ""); + if (err != OK) { + set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR); } else { ProjectSettings::CustomMap edited_settings; edited_settings["application/config/name"] = project_name->get_text(); @@ -530,8 +531,9 @@ public: ProjectSettings *current = memnew(ProjectSettings); - if (current->setup(project_path->get_text(), "")) { - set_message(TTR("Couldn't get project.godot in the project path."), MESSAGE_ERROR); + int err = current->setup(project_path->get_text(), ""); + if (err != OK) { + set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR); status_rect->show(); msg->show(); get_ok()->set_disabled(true); @@ -713,6 +715,12 @@ void ProjectManager::_update_project_buttons() { CanvasItem *item = Object::cast_to<CanvasItem>(scroll_children->get_child(i)); item->update(); + + Button *show = Object::cast_to<Button>(item->get_node(NodePath("project/path_box/show"))); + if (show) { + String current = item->get_meta("name"); + show->set_visible(selected_list.has(current)); + } } erase_btn->set_disabled(selected_list.size() < 1); @@ -1092,9 +1100,26 @@ void ProjectManager::_load_recent_projects() { title->add_color_override("font_color", font_color); title->set_clip_text(true); vb->add_child(title); + + HBoxContainer *path_hb = memnew(HBoxContainer); + path_hb->set_name("path_box"); + path_hb->set_h_size_flags(SIZE_EXPAND_FILL); + vb->add_child(path_hb); + + Button *show = memnew(Button); + show->set_name("show"); + show->set_icon(get_icon("Filesystem", "EditorIcons")); + show->set_flat(true); + show->set_modulate(Color(1, 1, 1, 0.5)); + path_hb->add_child(show); + show->connect("pressed", this, "_show_project", varray(path)); + show->set_tooltip(TTR("Show In File Manager")); + show->set_visible(false); + Label *fpath = memnew(Label(path)); fpath->set_name("path"); - vb->add_child(fpath); + path_hb->add_child(fpath); + fpath->set_h_size_flags(SIZE_EXPAND_FILL); fpath->set_modulate(Color(1, 1, 1, 0.5)); fpath->add_color_override("font_color", font_color); fpath->set_clip_text(true); @@ -1124,7 +1149,7 @@ void ProjectManager::_on_project_created(const String &dir) { bool has_already = false; for (int i = 0; i < scroll_children->get_child_count(); i++) { HBoxContainer *hb = Object::cast_to<HBoxContainer>(scroll_children->get_child(i)); - Label *fpath = Object::cast_to<Label>(hb->get_node(NodePath("project/path"))); + Label *fpath = Object::cast_to<Label>(hb->get_node(NodePath("project/path_box/path"))); if (fpath->get_text() == dir) { has_already = true; break; @@ -1142,7 +1167,7 @@ void ProjectManager::_on_project_created(const String &dir) { void ProjectManager::_update_scroll_position(const String &dir) { for (int i = 0; i < scroll_children->get_child_count(); i++) { HBoxContainer *hb = Object::cast_to<HBoxContainer>(scroll_children->get_child(i)); - Label *fpath = Object::cast_to<Label>(hb->get_node(NodePath("project/path"))); + Label *fpath = Object::cast_to<Label>(hb->get_node(NodePath("project/path_box/path"))); if (fpath->get_text() == dir) { last_clicked = hb->get_meta("name"); selected_list.clear(); @@ -1260,6 +1285,11 @@ void ProjectManager::_run_project() { } } +void ProjectManager::_show_project(const String &p_path) { + + OS::get_singleton()->shell_open(String("file://") + p_path); +} + void ProjectManager::_scan_dir(DirAccess *da, float pos, float total, List<String> *r_projects) { List<String> subdirs; @@ -1448,6 +1478,7 @@ void ProjectManager::_bind_methods() { ClassDB::bind_method("_open_project_confirm", &ProjectManager::_open_project_confirm); ClassDB::bind_method("_run_project", &ProjectManager::_run_project); ClassDB::bind_method("_run_project_confirm", &ProjectManager::_run_project_confirm); + ClassDB::bind_method("_show_project", &ProjectManager::_show_project); ClassDB::bind_method("_scan_projects", &ProjectManager::_scan_projects); ClassDB::bind_method("_scan_begin", &ProjectManager::_scan_begin); ClassDB::bind_method("_import_project", &ProjectManager::_import_project); diff --git a/editor/project_manager.h b/editor/project_manager.h index 23ffa2f857..a9d23b1f71 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -85,6 +85,7 @@ class ProjectManager : public Control { void _run_project_confirm(); void _open_project(); void _open_project_confirm(); + void _show_project(const String &p_path); void _import_project(); void _new_project(); void _rename_project(); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 452cf5b060..9364b0ff2c 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -176,6 +176,8 @@ public: void popup_project_settings(); void set_plugins_page(); + EditorAutoloadSettings *get_autoload_settings() { return autoload_settings; } + TabContainer *get_tabs(); void queue_save(); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 86ab84909e..af44c56074 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -1873,6 +1873,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { sc->set_v_size_flags(SIZE_EXPAND_FILL); stack_dump = memnew(Tree); + stack_dump->set_allow_reselect(true); stack_dump->set_columns(1); stack_dump->set_column_titles_visible(true); stack_dump->set_column_title(0, TTR("Stack Frames")); diff --git a/main/main.cpp b/main/main.cpp index ffe1215b1c..4d7273b4e4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -125,6 +125,8 @@ static bool editor = false; static bool show_help = false; static bool disable_render_loop = false; static int fixed_fps = -1; +static bool auto_build_solutions = false; +static bool auto_quit = false; static OS::ProcessID allow_focus_steal_pid = 0; @@ -261,6 +263,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --export-debug Use together with --export, enables debug mode for the template.\n"); OS::get_singleton()->print(" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n"); OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n"); + OS::get_singleton()->print(" --build-solutions Builds the scripting solutions (IE. C#).\n"); #ifdef DEBUG_METHODS_ENABLED OS::get_singleton()->print(" --gdnative-generate-json-api Generate JSON dump of the Godot API for GDNative bindings.\n"); #endif @@ -526,6 +529,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "-p" || I->get() == "--project-manager") { // starts project manager project_manager = true; + } else if (I->get() == "--build-solutions") { // Build the scripting solution such C# + + auto_build_solutions = true; #endif } else if (I->get() == "--no-window") { // disable window creation, Windows only @@ -552,6 +558,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } } else if (I->get() == "-u" || I->get() == "--upwards") { // scan folders upwards upwards = true; + } else if (I->get() == "--quit" || I->get() == "-q") { // Auto quit at the end of the first main loop iteration + auto_quit = true; } else if (I->get().ends_with("project.godot")) { String path; String file = I->get(); @@ -780,6 +788,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->add_logger(memnew(RotatedFileLogger(base_path, max_files))); } +#ifdef TOOLS_ENABLED if (editor) { Engine::get_singleton()->set_editor_hint(true); main_args.push_back("--editor"); @@ -787,23 +796,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph init_maximized = true; video_mode.maximized = true; } - use_custom_res = false; } - if (bool(ProjectSettings::get_singleton()->get("application/run/disable_stdout"))) { - quiet_stdout = true; - } - if (bool(ProjectSettings::get_singleton()->get("application/run/disable_stderr"))) { - _print_error_enabled = false; - }; - - if (quiet_stdout) - _print_line_enabled = false; - - OS::get_singleton()->set_cmdline(execpath, main_args); - -#ifdef TOOLS_ENABLED - if (!project_manager) { // Determine if the project manager should be requested project_manager = @@ -812,17 +806,26 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph (!ProjectSettings::get_singleton()->has_setting("application/run/main_scene") || String(ProjectSettings::get_singleton()->get("application/run/main_scene")) == ""); } - - if (project_manager) { - use_custom_res = false; //project manager (run without arguments) - } - #endif - if (editor) + if (editor || project_manager) { + use_custom_res = false; input_map->load_default(); //keys for editor - else + } else { input_map->load_from_globals(); //keys for game + } + + if (bool(ProjectSettings::get_singleton()->get("application/run/disable_stdout"))) { + quiet_stdout = true; + } + if (bool(ProjectSettings::get_singleton()->get("application/run/disable_stderr"))) { + _print_error_enabled = false; + }; + + if (quiet_stdout) + _print_line_enabled = false; + + OS::get_singleton()->set_cmdline(execpath, main_args); //if (video_driver == "") // useless for now, so removing // video_driver = GLOBAL_DEF("display/driver/name", Variant((const char *)OS::get_singleton()->get_video_driver_name(0))); @@ -1104,7 +1107,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { MAIN_PRINT("Main: Create bootsplash"); #if defined(TOOLS_ENABLED) && !defined(NO_EDITOR_SPLASH) - Ref<Image> splash = editor ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png)); + Ref<Image> splash = (editor || project_manager) ? memnew(Image(boot_splash_editor_png)) : memnew(Image(boot_splash_png)); #else Ref<Image> splash = memnew(Image(boot_splash_png)); #endif @@ -1130,7 +1133,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon", PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp")); if (bool(GLOBAL_DEF("display/window/handheld/emulate_touchscreen", false))) { - if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton() && !editor) { + if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton() && !(editor || project_manager)) { //only if no touchscreen ui hint, set emulation InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); if (id) @@ -1218,7 +1221,6 @@ bool Main::start() { ERR_FAIL_COND_V(!_start_success, false); bool hasicon = false; - bool editor = false; String doc_tool; List<String> removal_docs; bool doc_base = true; @@ -1452,7 +1454,7 @@ bool Main::start() { { } - if (!editor) { + if (!editor && !project_manager) { //standard helpers that can be changed from main config String stretch_mode = GLOBAL_DEF("display/window/stretch/mode", "disabled"); @@ -1563,7 +1565,7 @@ bool Main::start() { #endif } - if (!project_manager && !editor) { + if (!project_manager && !editor) { // game if (game_path != "" || script != "") { //autoload List<PropertyInfo> props; @@ -1645,7 +1647,6 @@ bool Main::start() { sml->get_root()->add_child(E->get()); } - //singletons } if (game_path != "") { @@ -1855,7 +1856,16 @@ bool Main::iteration() { target_ticks = MIN(MAX(target_ticks, current_ticks - time_step), current_ticks + time_step); } - return exit; +#ifdef TOOLS_ENABLED + if (auto_build_solutions) { + auto_build_solutions = false; + if (!EditorNode::get_singleton()->call_build()) { + ERR_FAIL_V(true); + } + } +#endif + + return exit || auto_quit; } void Main::force_redraw() { diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp index 4164450cd2..8707096038 100644 --- a/modules/bullet/btRayShape.cpp +++ b/modules/bullet/btRayShape.cpp @@ -54,6 +54,11 @@ void btRayShape::setLength(btScalar p_length) { reload_cache(); } +void btRayShape::setSlipsOnSlope(bool p_slipsOnSlope) { + + slipsOnSlope = p_slipsOnSlope; +} + btVector3 btRayShape::localGetSupportingVertex(const btVector3 &vec) const { return localGetSupportingVertexWithoutMargin(vec) + (m_shapeAxis * m_collisionMargin); } diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h index 99a9412dbe..a44c30db4b 100644 --- a/modules/bullet/btRayShape.h +++ b/modules/bullet/btRayShape.h @@ -44,6 +44,7 @@ ATTRIBUTE_ALIGNED16(class) btRayShape : public btConvexInternalShape { btScalar m_length; + bool slipsOnSlope; /// The default axis is the z btVector3 m_shapeAxis; @@ -59,6 +60,9 @@ public: void setLength(btScalar p_length); btScalar getLength() const { return m_length; } + void setSlipsOnSlope(bool p_slipOnSlope); + bool getSlipsOnSlope() const { return slipsOnSlope; } + const btTransform &getSupportPoint() const { return m_cacheSupportPoint; } const btScalar &getScaledLength() const { return m_cacheScaledLength; } diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index 4a511b39a7..53d0ab7e3c 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -100,14 +100,16 @@ void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *bo if (btResult.hasHit()) { - btVector3 ray_normal(ray_transform.getOrigin() - to.getOrigin()); - ray_normal.normalize(); btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1)); if (depth >= -RAY_STABILITY_MARGIN) depth = 0; - resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, depth); + if (ray_shape->getSlipsOnSlope()) + resultOut->addContactPoint(btResult.m_hitNormalWorld, btResult.m_hitPointWorld, depth); + else { + resultOut->addContactPoint((ray_transform.getOrigin() - to.getOrigin()).normalize(), btResult.m_hitPointWorld, depth); + } } } diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index f96218ef46..75b4cc054a 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -114,10 +114,18 @@ Transform BulletPhysicsDirectBodyState::get_transform() const { return body->get_transform(); } +void BulletPhysicsDirectBodyState::add_central_force(const Vector3 &p_force) { + body->apply_central_force(p_force); +} + void BulletPhysicsDirectBodyState::add_force(const Vector3 &p_force, const Vector3 &p_pos) { body->apply_force(p_force, p_pos); } +void BulletPhysicsDirectBodyState::add_torque(const Vector3 &p_torque) { + body->apply_torque(p_torque); +} + void BulletPhysicsDirectBodyState::apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) { body->apply_impulse(p_pos, p_j); } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index c4a9676bdd..2d529f6dc7 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -110,7 +110,9 @@ public: virtual void set_transform(const Transform &p_transform); virtual Transform get_transform() const; + virtual void add_central_force(const Vector3 &p_force); virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos); + virtual void add_torque(const Vector3 &p_torque); virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j); virtual void apply_torque_impulse(const Vector3 &p_j); diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index c6b9695d96..5d8d391bd9 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -135,8 +135,10 @@ btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<rea return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges)); } -btRayShape *ShapeBullet::create_shape_ray(real_t p_length) { - return bulletnew(btRayShape(p_length)); +btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) { + btRayShape *r(bulletnew(btRayShape(p_length))); + r->setSlipsOnSlope(p_slips_on_slope); + return r; } /* PLANE */ @@ -435,25 +437,33 @@ btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_impli /* Ray shape */ RayShapeBullet::RayShapeBullet() : ShapeBullet(), - length(1) {} + length(1), + slips_on_slope(false) {} void RayShapeBullet::set_data(const Variant &p_data) { - setup(p_data); + + Dictionary d = p_data; + setup(d["length"], d["slips_on_slope"]); } Variant RayShapeBullet::get_data() const { - return length; + + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + return d; } PhysicsServer::ShapeType RayShapeBullet::get_type() const { return PhysicsServer::SHAPE_RAY; } -void RayShapeBullet::setup(real_t p_length) { +void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) { length = p_length; + slips_on_slope = p_slips_on_slope; notifyShapeChanged(); } btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) { - return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_margin)); + return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_margin, slips_on_slope)); } diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index e04a3c808a..2acba90e36 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -86,7 +86,7 @@ public: static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1)); static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1)); static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); - static class btRayShape *create_shape_ray(real_t p_length); + static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope); }; class PlaneShapeBullet : public ShapeBullet { @@ -216,6 +216,7 @@ class RayShapeBullet : public ShapeBullet { public: real_t length; + bool slips_on_slope; RayShapeBullet(); @@ -225,6 +226,6 @@ public: virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: - void setup(real_t p_length); + void setup(real_t p_length, bool p_slips_on_slope); }; #endif diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index f69b632e76..a34a650a4d 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -87,6 +87,6 @@ if env['builtin_freetype']: # Godot source files env.add_source_files(env.modules_sources, "*.cpp") -env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) +env.Append(CCFLAGS=['-DFREETYPE_ENABLED', '-DFT_CONFIG_OPTION_USE_PNG']) Export('env') diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index c067d5409f..278585cb01 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -358,13 +358,16 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); } break; case MATH_RANDOMIZE: { + VALIDATE_ARG_COUNT(0); Math::randomize(); r_ret = Variant(); } break; case MATH_RAND: { + VALIDATE_ARG_COUNT(0); r_ret = Math::rand(); } break; case MATH_RANDF: { + VALIDATE_ARG_COUNT(0); r_ret = Math::randf(); } break; case MATH_RANDOM: { @@ -593,7 +596,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ r_ret = String(result); } break; case TEXT_STR: { + if (p_arg_count < 1) { + r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + r_ret = Variant(); + return; + } String str; for (int i = 0; i < p_arg_count; i++) { @@ -1180,6 +1189,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ } break; case PRINT_STACK: { + VALIDATE_ARG_COUNT(0); ScriptLanguage *script = GDScriptLanguage::get_singleton(); for (int i = 0; i < script->debug_get_stack_level_count(); i++) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 1392323d56..cd752a786c 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -456,9 +456,9 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (!validating) { //this can be too slow for just validating code - if (for_completion && ScriptCodeCompletionCache::get_singleton()) { + if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) { res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); - } else { // essential; see issue 15902 + } else if (!for_completion || FileAccess::exists(path)) { res = ResourceLoader::load(path); } if (!res.is_valid()) { @@ -576,18 +576,47 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (identifier == StringName()) { - _set_error("Built-in type constant expected after '.'"); + _set_error("Built-in type constant or static function expected after '.'"); return NULL; } if (!Variant::has_numeric_constant(bi_type, identifier)) { - _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); - return NULL; - } + if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN && + Variant::is_method_const(bi_type, identifier) && + Variant::get_method_return_type(bi_type, identifier) == bi_type) { + + tokenizer->advance(); + + OperatorNode *construct = alloc_node<OperatorNode>(); + construct->op = OperatorNode::OP_CALL; + + TypeNode *tn = alloc_node<TypeNode>(); + tn->vtype = bi_type; + construct->arguments.push_back(tn); + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = OperatorNode::OP_CALL; + op->arguments.push_back(construct); + + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name = identifier; + op->arguments.push_back(id); + + if (!_parse_arguments(op, op->arguments, p_static, true)) + return NULL; + + expr = op; + } else { - ConstantNode *cn = alloc_node<ConstantNode>(); - cn->value = Variant::get_numeric_constant_value(bi_type, identifier); - expr = cn; + _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); + return NULL; + } + } else { + + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->value = Variant::get_numeric_constant_value(bi_type, identifier); + expr = cn; + } } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_PARENTHESIS_OPEN && tokenizer->is_token_literal()) { // We check with is_token_literal, as this allows us to use match/sync/etc. as a name @@ -1516,11 +1545,11 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to String errwhere; if (op->arguments[0]->type == Node::TYPE_TYPE) { TypeNode *tn = static_cast<TypeNode *>(op->arguments[0]); - errwhere = "'" + Variant::get_type_name(tn->vtype) + "'' constructor"; + errwhere = "'" + Variant::get_type_name(tn->vtype) + "' constructor"; } else { GDScriptFunctions::Function func = static_cast<BuiltInFunctionNode *>(op->arguments[0])->function; - errwhere = String("'") + GDScriptFunctions::get_func_name(func) + "'' intrinsic function"; + errwhere = String("'") + GDScriptFunctions::get_func_name(func) + "' intrinsic function"; } switch (ce.error) { @@ -4356,8 +4385,6 @@ Error GDScriptParser::_parse(const String &p_base_path) { base_path = p_base_path; - clear(); - //assume class ClassNode *main_class = alloc_node<ClassNode>(); main_class->initializer = alloc_node<BlockNode>(); @@ -4382,17 +4409,7 @@ Error GDScriptParser::_parse(const String &p_base_path) { Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path, const String &p_self_path) { - for_completion = false; - validating = false; - completion_type = COMPLETION_NONE; - completion_node = NULL; - completion_class = NULL; - completion_function = NULL; - completion_block = NULL; - completion_found = false; - current_block = NULL; - current_class = NULL; - current_function = NULL; + clear(); self_path = p_self_path; GDScriptTokenizerBuffer *tb = memnew(GDScriptTokenizerBuffer); @@ -4406,16 +4423,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion) { - completion_type = COMPLETION_NONE; - completion_node = NULL; - completion_class = NULL; - completion_function = NULL; - completion_block = NULL; - completion_found = false; - current_block = NULL; - current_class = NULL; - - current_function = NULL; + clear(); self_path = p_self_path; GDScriptTokenizerText *tt = memnew(GDScriptTokenizerText); diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index df0985f6ac..69040a3df8 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -230,11 +230,12 @@ String GDMonoMethod::get_signature_desc(bool p_namespaces) const { } void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const { - const char *_names[params_count]; - mono_method_get_param_names(mono_method, _names); + const char *_names = memnew_arr(char, params_count); + mono_method_get_param_names(mono_method, &_names); for (int i = 0; i < params_count; ++i) { - names.push_back(StringName(_names[i])); + names.push_back(StringName(&_names[i])); } + memdelete_arr(_names); } void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const { diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index aa282338cb..0ebafa022d 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -254,7 +254,6 @@ env_libvpx = env.Clone() env_libvpx.Append(CPPPATH=[libvpx_dir]) cpu_bits = env["bits"] -osx_fat = (env["platform"] == 'osx' and cpu_bits == 'fat') webm_cpu_x86 = False webm_cpu_arm = False if env["platform"] == 'uwp': @@ -269,11 +268,8 @@ else: is_android_x86 = (env["platform"] == 'android' and env["android_arch"] == 'x86') if is_android_x86: cpu_bits = '32' - if osx_fat: - webm_cpu_x86 = True - else: - webm_cpu_x86 = not is_x11_or_server_arm and (cpu_bits == '32' or cpu_bits == '64') and (env["platform"] == 'windows' or env["platform"] == 'x11' or env["platform"] == 'osx' or env["platform"] == 'haiku' or is_android_x86 or is_ios_x86) - webm_cpu_arm = is_x11_or_server_arm or (not is_ios_x86 and env["platform"] == 'iphone') or (not is_android_x86 and env["platform"] == 'android') + webm_cpu_x86 = not is_x11_or_server_arm and (cpu_bits == '32' or cpu_bits == '64') and (env["platform"] == 'windows' or env["platform"] == 'x11' or env["platform"] == 'osx' or env["platform"] == 'haiku' or is_android_x86 or is_ios_x86) + webm_cpu_arm = is_x11_or_server_arm or (not is_ios_x86 and env["platform"] == 'iphone') or (not is_android_x86 and env["platform"] == 'android') if webm_cpu_x86: import subprocess @@ -303,28 +299,22 @@ if webm_cpu_x86: webm_simd_optimizations = False if webm_cpu_x86: - if osx_fat: - #'osx' platform only: run python script which will compile using 'yasm' command and then merge 32-bit and 64-bit using 'lipo' command - env_libvpx["AS"] = 'python modules/webm/libvpx/yasm_osx_fat.py' - env_libvpx["ASFLAGS"] = '-I' + libvpx_dir[1:] - env_libvpx["ASCOM"] = '$AS $ASFLAGS $TARGET $SOURCES' + if env["platform"] == 'windows' or env["platform"] == 'uwp': + env_libvpx["ASFORMAT"] = 'win' + elif env["platform"] == 'osx' or env["platform"] == "iphone": + env_libvpx["ASFORMAT"] = 'macho' else: - if env["platform"] == 'windows' or env["platform"] == 'uwp': - env_libvpx["ASFORMAT"] = 'win' - elif env["platform"] == 'osx' or env["platform"] == "iphone": - env_libvpx["ASFORMAT"] = 'macho' - else: - env_libvpx["ASFORMAT"] = 'elf' - env_libvpx["ASFORMAT"] += cpu_bits - - env_libvpx["AS"] = 'yasm' - env_libvpx["ASFLAGS"] = '-I' + libvpx_dir[1:] + ' -f $ASFORMAT -D $ASCPU' - env_libvpx["ASCOM"] = '$AS $ASFLAGS -o $TARGET $SOURCES' + env_libvpx["ASFORMAT"] = 'elf' + env_libvpx["ASFORMAT"] += cpu_bits + + env_libvpx["AS"] = 'yasm' + env_libvpx["ASFLAGS"] = '-I' + libvpx_dir[1:] + ' -f $ASFORMAT -D $ASCPU' + env_libvpx["ASCOM"] = '$AS $ASFLAGS -o $TARGET $SOURCES' - if cpu_bits == '32': - env_libvpx["ASCPU"] = 'X86_32' - elif cpu_bits == '64': - env_libvpx["ASCPU"] = 'X86_64' + if cpu_bits == '32': + env_libvpx["ASCPU"] = 'X86_32' + elif cpu_bits == '64': + env_libvpx["ASCPU"] = 'X86_64' env_libvpx.Append(CCFLAGS=['-DWEBM_X86ASM']) @@ -375,7 +365,7 @@ if webm_cpu_x86: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_intrin_x86) env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86asm) - if cpu_bits == '64' or osx_fat: + if cpu_bits == '64': env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86_64asm) elif webm_cpu_arm: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm) diff --git a/modules/webm/libvpx/yasm_osx_fat.py b/modules/webm/libvpx/yasm_osx_fat.py deleted file mode 100644 index 0065e2766c..0000000000 --- a/modules/webm/libvpx/yasm_osx_fat.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -import sys -import os - -includes = sys.argv[1] -output_file = sys.argv[2] -input_file = sys.argv[3] - -can_remove = {} - -lipo_command = '' - -exit_code = 1 - -for arch in ['32', '64']: - if arch == '32' and input_file.endswith('x86_64.asm'): - can_remove[arch] = False - else: - command = 'yasm ' + includes + ' -f macho' + arch + ' -D X86_' + arch + ' -o ' + output_file + '.' + arch + ' ' + input_file - print(command) - if os.system(command) == 0: - lipo_command += output_file + '.' + arch + ' ' - can_remove[arch] = True - else: - can_remove[arch] = False - -if lipo_command != '': - lipo_command = 'lipo -create ' + lipo_command + '-output ' + output_file - print(lipo_command) - if os.system(lipo_command) == 0: - exit_code = 0 - -for arch in ['32', '64']: - if can_remove[arch]: - os.remove(output_file + '.' + arch) - -sys.exit(exit_code) diff --git a/platform/osx/SCsub b/platform/osx/SCsub index 5efe2d0b22..4dfa46528a 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -7,10 +7,10 @@ def make_debug(target, source, env): if (env["macports_clang"] != 'no'): mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") mpclangver = env["macports_clang"] - os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil %s -o %s.dSYM' % (target[0], target[0])) + os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil {0} -o {0}.dSYM'.format(target[0])) else: - os.system('dsymutil %s -o %s.dSYM' % (target[0], target[0])) - os.system('strip -u -r %s' % (target[0])) + os.system('dsymutil {0} -o {0}.dSYM'.format(target[0])) + os.system('strip -u -r {0}'.format(target[0])) files = [ 'crash_handler_osx.mm', diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index d757674a9b..99ce25adfb 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -35,8 +35,7 @@ #include <string.h> #include <unistd.h> -// Note: Dump backtrace in 32bit mode is getting a bus error on the fgets by the ->execute, so enable only on 64bit -#if defined(DEBUG_ENABLED) && defined(__x86_64__) +#if defined(DEBUG_ENABLED) #define CRASH_HANDLER_ENABLED 1 #endif @@ -50,13 +49,8 @@ #include <mach-o/dyld.h> #include <mach-o/getsect.h> -#ifdef __x86_64__ static uint64_t load_address() { const struct segment_command_64 *cmd = getsegbyname("__TEXT"); -#else -static uint32_t load_address() { - const struct segment_command *cmd = getsegbyname("__TEXT"); -#endif char full_path[1024]; uint32_t size = sizeof(full_path); @@ -120,11 +114,7 @@ static void handle_crash(int sig) { args.push_back("-o"); args.push_back(_execpath); args.push_back("-arch"); -#ifdef __x86_64__ args.push_back("x86_64"); -#else - args.push_back("i386"); -#endif args.push_back("-l"); snprintf(str, 1024, "%p", load_addr); args.push_back(str); diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 5f33100e42..6a9fd6b134 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -57,22 +57,15 @@ def configure(env): ## Architecture - is64 = sys.maxsize > 2**32 - if (env["bits"] == "default"): - env["bits"] = "64" if is64 else "32" + # Mac OS X no longer runs on 32-bit since 10.7 which is unsupported since 2014 + # As such, we only support 64-bit + env["bits"] = 64 ## Compiler configuration if "OSXCROSS_ROOT" not in os.environ: # regular native build - if (env["bits"] == "fat"): - env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - elif (env["bits"] == "32"): - env.Append(CCFLAGS=['-arch', 'i386']) - env.Append(LINKFLAGS=['-arch', 'i386']) - else: # 64-bit, default - env.Append(CCFLAGS=['-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'x86_64']) + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) if (env["macports_clang"] != 'no'): mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") mpclangver = env["macports_clang"] @@ -86,14 +79,7 @@ def configure(env): else: # osxcross build root = os.environ.get("OSXCROSS_ROOT", 0) - if env["bits"] == "fat": - basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" - env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - elif env["bits"] == "32": - basecmd = root + "/target/bin/i386-apple-" + env["osxcross_sdk"] + "-" - else: # 64-bit, default - basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" + basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" ccache_path = os.environ.get("CCACHE") if ccache_path == None: diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index c4efa1f0ff..7985a241e4 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -101,15 +101,7 @@ void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> r_features->push_back("etc2"); } - int bits = p_preset->get("application/bits_mode"); - - if (bits == 0 || bits == 1) { - r_features->push_back("64"); - } - - if (bits == 0 || bits == 2) { - r_features->push_back("32"); - } + r_features->push_back("64"); } void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { @@ -125,7 +117,6 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); #ifdef OSX_ENABLED @@ -323,11 +314,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); int ret = unzGoToFirstFile(src_pkg_zip); - String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + "."; - int bits_mode = p_preset->get("application/bits_mode"); - binary_to_use += String(bits_mode == 0 ? "fat" : bits_mode == 1 ? "64" : "32"); + String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; - print_line("binary: " + binary_to_use); String pkg_name; if (p_preset->get("application/name") != "") pkg_name = p_preset->get("application/name"); // app_name diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 8965b80fb7..ed3827353d 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -9,9 +9,9 @@ def make_debug_mingw(target, source, env): mingw_prefix = env["mingw_prefix_32"] else: mingw_prefix = env["mingw_prefix_64"] - os.system(mingw_prefix + 'objcopy --only-keep-debug %s %s.debugsymbols' % (target[0], target[0])) - os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded %s' % (target[0])) - os.system(mingw_prefix + 'objcopy --add-gnu-debuglink=%s.debugsymbols %s' % (target[0], target[0])) + os.system(mingw_prefix + 'objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) + os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded {0}'.format(target[0])) + os.system(mingw_prefix + 'objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) common_win = [ "context_gl_win.cpp", diff --git a/platform/x11/SCsub b/platform/x11/SCsub index b18757337a..d0f77892ef 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -4,9 +4,9 @@ import os Import('env') def make_debug(target, source, env): - os.system('objcopy --only-keep-debug %s %s.debugsymbols' % (target[0], target[0])) - os.system('strip --strip-debug --strip-unneeded %s' % (target[0])) - os.system('objcopy --add-gnu-debuglink=%s.debugsymbols %s' % (target[0], target[0])) + os.system('objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) + os.system('strip --strip-debug --strip-unneeded {0}'.format(target[0])) + os.system('objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) common_x11 = [ "context_gl_x11.cpp", diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 2d8e32137f..2027667af3 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -32,6 +32,7 @@ #include "drivers/gles3/rasterizer_gles3.h" #include "errno.h" #include "key_mapping_x11.h" +#include "os/dir_access.h" #include "print_string.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" @@ -730,6 +731,16 @@ void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) con } void OS_X11::set_wm_fullscreen(bool p_enabled) { + if (p_enabled && !get_borderless_window()) { + // remove decorations if the window is not already borderless + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = 0; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } + if (p_enabled && !is_window_resizable()) { // Set the window as resizable to prevent window managers to ignore the fullscreen state flag. XSizeHints *xsh; @@ -2585,48 +2596,66 @@ static String get_mountpoint(const String &p_path) { } Error OS_X11::move_to_trash(const String &p_path) { - String trashcan = ""; + String trash_can = ""; String mnt = get_mountpoint(p_path); + // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can. if (mnt != "") { String path(mnt + "/.Trash-" + itos(getuid()) + "/files"); struct stat s; if (!stat(path.utf8().get_data(), &s)) { - trashcan = path; + trash_can = path; } } - if (trashcan == "") { + // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can. + if (trash_can == "") { char *dhome = getenv("XDG_DATA_HOME"); if (dhome) { - trashcan = String(dhome) + "/Trash/files"; + trash_can = String(dhome) + "/Trash/files"; } } - if (trashcan == "") { + // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can. + if (trash_can == "") { char *home = getenv("HOME"); if (home) { - trashcan = String(home) + "/.local/share/Trash/files"; + trash_can = String(home) + "/.local/share/Trash/files"; } } - if (trashcan == "") { - ERR_PRINTS("move_to_trash: Could not determine trashcan location"); + // Issue an error if none of the previous locations is appropriate for the trash can. + if (trash_can == "") { + ERR_PRINTS("move_to_trash: Could not determine the trash can location"); return FAILED; } - List<String> args; - args.push_back("-p"); - args.push_back(trashcan); - Error err = execute("mkdir", args, true); - if (err == OK) { - List<String> args2; - args2.push_back(p_path); - args2.push_back(trashcan); - err = execute("mv", args2, true); + // Create needed directories for decided trash can location. + DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Error err = dir_access->make_dir_recursive(trash_can); + memdelete(dir_access); + + // Issue an error if trash can is not created proprely. + if (err != OK) { + ERR_PRINTS("move_to_trash: Could not create the trash can \"" + trash_can + "\""); + return err; } - return err; + // The trash can is successfully created, now move the given resource to it. + // Do not use DirAccess:rename() because it can't move files across multiple mountpoints. + List<String> mv_args; + mv_args.push_back(p_path); + mv_args.push_back(trash_can); + int retval; + err = execute("mv", mv_args, true, NULL, NULL, &retval); + + // Issue an error if "mv" failed to move the given resource to the trash can. + if (err != OK || retval != 0) { + ERR_PRINTS("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\""); + return FAILED; + } + + return OK; } OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const { diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index f290a181ec..824f50495b 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -34,7 +34,51 @@ #define NORMAL_SUFFIX "_normal" -//////////////////////////// +Dictionary AnimatedSprite::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = offset; + return state; +} + +void AnimatedSprite::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_offset(p_state["offset"]); +} + +void AnimatedSprite::_edit_set_pivot(const Point2 &p_pivot) { + set_offset(get_offset() - p_pivot); + set_position(get_transform().xform(p_pivot)); +} + +Point2 AnimatedSprite::_edit_get_pivot() const { + return Vector2(); +} + +bool AnimatedSprite::_edit_use_pivot() const { + return true; +} + +Rect2 AnimatedSprite::_edit_get_rect() const { + if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + return Node2D::_edit_get_rect(); + } + + Ref<Texture> t; + if (animation) + t = frames->get_frame(animation, frame); + if (t.is_null()) + return Node2D::_edit_get_rect(); + Size2 s = t->get_size(); + + Point2 ofs = offset; + if (centered) + ofs -= s / 2; + + if (s == Size2(0, 0)) + s = Size2(1, 1); + + return Rect2(ofs, s); +} void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) { @@ -248,20 +292,6 @@ SpriteFrames::SpriteFrames() { add_animation(SceneStringNames::get_singleton()->_default); } -void AnimatedSprite::_edit_set_pivot(const Point2 &p_pivot) { - - set_offset(p_pivot); -} - -Point2 AnimatedSprite::_edit_get_pivot() const { - - return get_offset(); -} -bool AnimatedSprite::_edit_use_pivot() const { - - return true; -} - void AnimatedSprite::_validate_property(PropertyInfo &property) const { if (!frames.is_valid()) @@ -491,29 +521,6 @@ bool AnimatedSprite::is_flipped_v() const { return vflip; } -Rect2 AnimatedSprite::_edit_get_rect() const { - - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { - return Node2D::_edit_get_rect(); - } - - Ref<Texture> t; - if (animation) - t = frames->get_frame(animation, frame); - if (t.is_null()) - return Node2D::_edit_get_rect(); - Size2i s = t->get_size(); - - Point2 ofs = offset; - if (centered) - ofs -= s / 2; - - if (s == Size2(0, 0)) - s = Size2(1, 1); - - return Rect2(ofs, s); -} - void AnimatedSprite::_res_changed() { set_frame(frame); diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 2c356d619f..a38adf792c 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -150,9 +150,13 @@ protected: virtual void _validate_property(PropertyInfo &property) const; public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; + virtual Rect2 _edit_get_rect() const; void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; @@ -182,8 +186,6 @@ public: void set_modulate(const Color &p_color); Color get_modulate() const; - virtual Rect2 _edit_get_rect() const; - virtual String get_configuration_warning() const; AnimatedSprite(); }; diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 7e5bea0511..68384b9f1e 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -230,7 +230,7 @@ public: // Used to resize/move/select the node virtual void _edit_set_rect(const Rect2 &p_rect){}; virtual Rect2 _edit_get_rect() const { return Rect2(-32, -32, 64, 64); }; - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return true; } + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return _edit_get_rect().has_point(p_point); } Rect2 _edit_get_item_and_children_rect() const; virtual bool _edit_use_rect() const { return false; }; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 8350e7c48a..d05c818ae1 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -336,7 +336,7 @@ String CollisionObject2D::get_configuration_warning() const { if (warning == String()) { warning += "\n"; } - warning += TTR("This node has no children shapes, so it can't interact with the space.\nConsider adding CollisionShape2D or CollisionPolygon2D children nodes to define its shape."); + warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."); } return warning; diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 999d8a2630..1220ff299c 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -33,35 +33,36 @@ #include "engine.h" #include "servers/visual_server.h" -void Light2D::_edit_set_pivot(const Point2 &p_pivot) { +Dictionary Light2D::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = get_texture_offset(); + return state; +} - set_texture_offset(p_pivot); +void Light2D::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_texture_offset(p_state["offset"]); } -Point2 Light2D::_edit_get_pivot() const { +void Light2D::_edit_set_pivot(const Point2 &p_pivot) { + set_position(get_transform().xform(p_pivot)); + set_texture_offset(get_texture_offset() - p_pivot); +} - return get_texture_offset(); +Point2 Light2D::_edit_get_pivot() const { + return Vector2(); } -bool Light2D::_edit_use_pivot() const { +bool Light2D::_edit_use_pivot() const { return true; } Rect2 Light2D::_edit_get_rect() const { - if (texture.is_null()) - return Rect2(0, 0, 1, 1); - - Size2i s; - - s = texture->get_size() * _scale; - Point2i ofs = texture_offset; - ofs -= s / 2; - - if (s == Size2(0, 0)) - s = Size2(1, 1); + return Node2D::_edit_get_rect(); - return Rect2(ofs, s); + Size2 s = texture->get_size() * _scale; + return Rect2(texture_offset - s / 2.0, s); } void Light2D::_update_light_visibility() { @@ -131,6 +132,7 @@ void Light2D::set_texture_offset(const Vector2 &p_offset) { texture_offset = p_offset; VS::get_singleton()->canvas_light_set_texture_offset(canvas_light, texture_offset); item_rect_changed(); + _change_notify("offset"); } Vector2 Light2D::get_texture_offset() const { diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index b216ad5e95..16d8c485d4 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -85,6 +85,9 @@ protected: static void _bind_methods(); public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 95a1cbfce3..95e24505be 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -46,10 +46,9 @@ Dictionary Node2D::_edit_get_state() const { } void Node2D::_edit_set_state(const Dictionary &p_state) { - Dictionary state = p_state; - pos = state["position"]; - angle = state["rotation"]; - _scale = state["scale"]; + pos = p_state["position"]; + angle = p_state["rotation"]; + _scale = p_state["scale"]; _update_transform(); _change_notify("rotation"); @@ -60,6 +59,8 @@ void Node2D::_edit_set_state(const Dictionary &p_state) { void Node2D::_edit_set_position(const Point2 &p_position) { pos = p_position; + _update_transform(); + _change_notify("position"); } Point2 Node2D::_edit_get_position() const { diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index f6cb796b10..e63dc4e515 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -31,8 +31,31 @@ #include "polygon_2d.h" #include "core/math/geometry.h" -Rect2 Polygon2D::_edit_get_rect() const { +Dictionary Polygon2D::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = offset; + return state; +} + +void Polygon2D::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_offset(p_state["offset"]); +} + +void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) { + set_position(get_transform().xform(p_pivot)); + set_offset(get_offset() - p_pivot); +} + +Point2 Polygon2D::_edit_get_pivot() const { + return Vector2(); +} + +bool Polygon2D::_edit_use_pivot() const { + return true; +} +Rect2 Polygon2D::_edit_get_rect() const { if (rect_cache_dirty) { int l = polygon.size(); PoolVector<Vector2>::Read r = polygon.read(); @@ -52,21 +75,7 @@ Rect2 Polygon2D::_edit_get_rect() const { bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { - return Geometry::is_point_in_polygon(p_point, Variant(polygon)); -} - -void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) { - - set_offset(p_pivot); -} - -Point2 Polygon2D::_edit_get_pivot() const { - - return get_offset(); -} -bool Polygon2D::_edit_use_pivot() const { - - return true; + return Geometry::is_point_in_polygon(p_point - get_offset(), Variant(polygon)); } void Polygon2D::_notification(int p_what) { @@ -324,6 +333,7 @@ void Polygon2D::set_offset(const Vector2 &p_offset) { offset = p_offset; rect_cache_dirty = true; update(); + _change_notify("offset"); } Vector2 Polygon2D::get_offset() const { diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index f62c78c55b..66f44cb3f1 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -59,6 +59,16 @@ protected: static void _bind_methods(); public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; + virtual Rect2 _edit_get_rect() const; + + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_polygon(const PoolVector<Vector2> &p_polygon); PoolVector<Vector2> get_polygon() const; @@ -98,15 +108,6 @@ public: void set_offset(const Vector2 &p_offset); Vector2 get_offset() const; - //editor stuff - - virtual void _edit_set_pivot(const Point2 &p_pivot); - virtual Point2 _edit_get_pivot() const; - virtual bool _edit_use_pivot() const; - - virtual Rect2 _edit_get_rect() const; - virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; - Polygon2D(); }; diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index 9c344b9581..17ca066fc0 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -34,17 +34,27 @@ #include "scene/main/viewport.h" #include "scene/scene_string_names.h" -void Sprite::_edit_set_pivot(const Point2 &p_pivot) { +Dictionary Sprite::_edit_get_state() const { + Dictionary state = Node2D::_edit_get_state(); + state["offset"] = offset; + return state; +} - set_offset(p_pivot); +void Sprite::_edit_set_state(const Dictionary &p_state) { + Node2D::_edit_set_state(p_state); + set_offset(p_state["offset"]); } -Point2 Sprite::_edit_get_pivot() const { +void Sprite::_edit_set_pivot(const Point2 &p_pivot) { + set_offset(get_offset() - p_pivot); + set_position(get_transform().xform(p_pivot)); +} - return get_offset(); +Point2 Sprite::_edit_get_pivot() const { + return Vector2(); } -bool Sprite::_edit_use_pivot() const { +bool Sprite::_edit_use_pivot() const { return true; } diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h index 261165bbf9..0422e0635f 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -65,6 +65,9 @@ protected: virtual void _validate_property(PropertyInfo &property) const; public: + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index fa4e6492a1..204aaef7ec 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -316,7 +316,7 @@ bool BakedLightmap::_bake_time(void *ud, float p_secs, float p_progress) { int mins_left = p_secs / 60; int secs_left = Math::fmod(p_secs, 60.0f); int percent = p_progress * 100; - bool abort = bake_step_function(btd->pass + percent, btd->text + " " + itos(percent) + "% (Time Left: " + itos(mins_left) + ":" + itos(secs_left) + "s)"); + bool abort = bake_step_function(btd->pass + percent, btd->text + " " + vformat(RTR("%d%%"), percent) + " " + vformat(RTR("(Time Left: %d:%02d s)"), mins_left, secs_left)); btd->last_step = time; if (abort) return true; diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index 07235b3da4..1d5d1b2afe 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -373,7 +373,7 @@ String CollisionObject::get_configuration_warning() const { if (warning == String()) { warning += "\n"; } - warning += TTR("This node has no children shapes, so it can't interact with the space.\nConsider adding CollisionShape or CollisionPolygon children nodes to define its shape."); + warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape or CollisionPolygon as a child to define its shape."); } return warning; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 5c814312dd..ad94c19c31 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -694,6 +694,10 @@ void RigidBody::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) { PhysicsServer::get_singleton()->body_apply_impulse(get_rid(), p_pos, p_impulse); } +void RigidBody::apply_torque_impulse(const Vector3 &p_impulse) { + PhysicsServer::get_singleton()->body_apply_torque_impulse(get_rid(), p_impulse); +} + void RigidBody::set_use_continuous_collision_detection(bool p_enable) { ccd = p_enable; @@ -833,6 +837,7 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody::set_axis_velocity); ClassDB::bind_method(D_METHOD("apply_impulse", "position", "impulse"), &RigidBody::apply_impulse); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidBody::apply_torque_impulse); ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody::set_sleeping); ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody::is_sleeping); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index c7556c0c5f..7899661d7f 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -242,6 +242,7 @@ public: Array get_colliding_bodies() const; void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); + void apply_torque_impulse(const Vector3 &p_impulse); virtual String get_configuration_warning() const; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 9dfd388c3d..5f541ea16a 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -307,6 +307,8 @@ void BaseButton::toggled(bool p_pressed) { } void BaseButton::set_disabled(bool p_disabled) { + if (status.disabled == p_disabled) + return; status.disabled = p_disabled; update(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index d8571a7767..a5883863cd 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -49,31 +49,41 @@ Dictionary Control::_edit_get_state() const { Dictionary s; - s["rect"] = get_rect(); s["rotation"] = get_rotation(); s["scale"] = get_scale(); + s["pivot"] = get_pivot_offset(); Array anchors; anchors.push_back(get_anchor(MARGIN_LEFT)); anchors.push_back(get_anchor(MARGIN_TOP)); anchors.push_back(get_anchor(MARGIN_RIGHT)); anchors.push_back(get_anchor(MARGIN_BOTTOM)); s["anchors"] = anchors; + Array margins; + margins.push_back(get_margin(MARGIN_LEFT)); + margins.push_back(get_margin(MARGIN_TOP)); + margins.push_back(get_margin(MARGIN_RIGHT)); + margins.push_back(get_margin(MARGIN_BOTTOM)); + s["margins"] = margins; return s; } void Control::_edit_set_state(const Dictionary &p_state) { Dictionary state = p_state; - Rect2 rect = state["rect"]; - set_position(rect.position); - set_size(rect.size); set_rotation(state["rotation"]); set_scale(state["scale"]); + set_pivot_offset(state["pivot"]); Array anchors = state["anchors"]; - set_anchor(MARGIN_LEFT, anchors[0]); - set_anchor(MARGIN_TOP, anchors[1]); - set_anchor(MARGIN_RIGHT, anchors[2]); - set_anchor(MARGIN_BOTTOM, anchors[3]); + data.anchor[MARGIN_LEFT] = anchors[0]; + data.anchor[MARGIN_TOP] = anchors[1]; + data.anchor[MARGIN_RIGHT] = anchors[2]; + data.anchor[MARGIN_BOTTOM] = anchors[3]; + Array margins = state["margins"]; + data.margin[MARGIN_LEFT] = margins[0]; + data.margin[MARGIN_TOP] = margins[1]; + data.margin[MARGIN_RIGHT] = margins[2]; + data.margin[MARGIN_BOTTOM] = margins[3]; + _size_changed(); } void Control::_edit_set_position(const Point2 &p_position) { @@ -85,19 +95,8 @@ Point2 Control::_edit_get_position() const { }; void Control::_edit_set_rect(const Rect2 &p_edit_rect) { - - Transform2D xform = _get_internal_transform(); - - Vector2 new_pos = xform.basis_xform(p_edit_rect.position); - - Vector2 pos = get_position() + new_pos; - - Rect2 new_rect = get_rect(); - new_rect.position = pos.snapped(Vector2(1, 1)); - new_rect.size = p_edit_rect.size.snapped(Vector2(1, 1)); - - set_position(new_rect.position); - set_size(new_rect.size); + set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1))); + set_size(p_edit_rect.size.snapped(Vector2(1, 1))); } Rect2 Control::_edit_get_rect() const { @@ -121,6 +120,9 @@ bool Control::_edit_use_rotation() const { } void Control::_edit_set_pivot(const Point2 &p_pivot) { + Vector2 delta_pivot = p_pivot - get_pivot_offset(); + Vector2 move = Vector2((cos(data.rotation) - 1.0) * delta_pivot.x - sin(data.rotation) * delta_pivot.y, sin(data.rotation) * delta_pivot.x + (cos(data.rotation) - 1.0) * delta_pivot.y); + set_position(get_position() + move); set_pivot_offset(p_pivot); } @@ -1297,7 +1299,8 @@ void Control::_size_changed() { new_size_cache.height = MAX(minimum_size.height, new_size_cache.height); } - if (get_viewport()->is_snap_controls_to_pixels_enabled()) { + // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot() + if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) { new_size_cache = new_size_cache.floor(); new_pos_cache = new_pos_cache.floor(); } @@ -1378,7 +1381,6 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range); } } - if (is_inside_tree()) { _size_changed(); } diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index fe85d04003..f7574ca2cd 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -961,12 +961,36 @@ void ItemList::_notification(int p_what) { Vector2 base_ofs = bg->get_offset(); base_ofs.y -= int(scroll_bar->get_value()); - Rect2 clip(Point2(), size - bg->get_minimum_size() + Vector2(0, scroll_bar->get_value())); + const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there + + int first_item_visible; + { + // do a binary search to find the first item whose rect reaches below clip.position.y + int lo = 0; + int hi = items.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + const Rect2 &rcache = items[mid].rect_cache; + if (rcache.position.y + rcache.size.y < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } + } + // we might have ended up with column 2, or 3, ..., so let's find the first column + while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) { + lo -= 1; + } + first_item_visible = lo; + } - for (int i = 0; i < items.size(); i++) { + for (int i = first_item_visible; i < items.size(); i++) { Rect2 rcache = items[i].rect_cache; + if (rcache.position.y > clip.position.y + clip.size.y) + break; // done + if (!clip.intersects(rcache)) continue; @@ -1138,8 +1162,28 @@ void ItemList::_notification(int p_what) { } } - for (int i = 0; i < separators.size(); i++) { - draw_line(Vector2(bg->get_margin(MARGIN_LEFT), base_ofs.y + separators[i]), Vector2(width, base_ofs.y + separators[i]), guide_color); + int first_visible_separator = 0; + { + // do a binary search to find the first separator that is below clip_position.y + int lo = 0; + int hi = separators.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (separators[mid] < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } + } + first_visible_separator = lo; + } + + for (int i = first_visible_separator; i < separators.size(); i++) { + if (separators[i] > clip.position.y + clip.size.y) + break; // done + + const int y = base_ofs.y + separators[i]; + draw_line(Vector2(bg->get_margin(MARGIN_LEFT), y), Vector2(width, y), guide_color); } } } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 1a46921561..71c14810f6 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -318,8 +318,9 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items); - ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); + // "selected" property must come after "items", otherwise GH-10213 occurs + ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID"))); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index e8454e021f..f13950461b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -776,7 +776,6 @@ void TextEdit::_notification(int p_what) { j--; } if (escaped) { - j--; cc = '\\'; continue; } diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index e9d5ca969e..23c6dc200b 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -186,10 +186,25 @@ Error DynamicFontAtSize::_load() { ERR_FAIL_COND_V( error, ERR_INVALID_PARAMETER ); }*/ - error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + if (FT_HAS_COLOR(face)) { + int best_match = 0; + int diff = ABS(id.size - face->available_sizes[0].width); + scale_color_font = float(id.size) / face->available_sizes[0].width; + for (int i = 1; i < face->num_fixed_sizes; i++) { + int ndiff = ABS(id.size - face->available_sizes[i].width); + if (ndiff < diff) { + best_match = i; + diff = ndiff; + scale_color_font = float(id.size) / face->available_sizes[i].width; + } + } + error = FT_Select_Size(face, best_match); + } else { + error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + } - ascent = (face->size->metrics.ascender >> 6) / oversampling; - descent = (-face->size->metrics.descender >> 6) / oversampling; + ascent = (face->size->metrics.ascender >> 6) / oversampling * scale_color_font; + descent = (-face->size->metrics.descender >> 6) / oversampling * scale_color_font; linegap = 0; texture_flags = 0; if (id.mipmaps) @@ -286,7 +301,6 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const V ret.x += (delta.x >> 6) / oversampling; } } - return ret; } @@ -328,14 +342,18 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT if (!ch->found) continue; - Point2 cpos = p_pos; cpos.x += ch->h_align; - cpos.y -= get_ascent(); + cpos.y -= fb->get_ascent(); cpos.y += ch->v_align; ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= fb->textures.size(), 0); - if (ch->texture_idx != -1) - VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size), fb->textures[ch->texture_idx].texture->get_rid(), ch->rect_uv, p_modulate, false, RID(), false); + if (ch->texture_idx != -1) { + Color modulate = p_modulate; + if (FT_HAS_COLOR(fb->face)) { + modulate.r = modulate.g = modulate.b = 1; + } + VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size * Vector2(fb->scale_color_font, fb->scale_color_font)), fb->textures[ch->texture_idx].texture->get_rid(), ch->rect_uv, modulate, false, RID(), false); + } advance = ch->advance; used_fallback = true; break; @@ -356,8 +374,13 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT cpos.y -= get_ascent(); cpos.y += c->v_align; ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0); - if (c->texture_idx != -1) - VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect_uv, p_modulate, false, RID(), false); + if (c->texture_idx != -1) { + Color modulate = p_modulate; + if (FT_HAS_COLOR(face)) { + modulate.r = modulate.g = modulate.b = 1; + } + VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size * Vector2(scale_color_font, scale_color_font)), textures[c->texture_idx].texture->get_rid(), c->rect_uv, modulate, false, RID(), false); + } advance = c->advance; //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2()); } @@ -431,9 +454,9 @@ void DynamicFontAtSize::_update_char(CharType p_char) { char_map[p_char] = ch; return; } - int error = FT_Load_Char(face, p_char, FT_LOAD_RENDER | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); if (!error) { - error = FT_Render_Glyph(face->glyph, ft_render_mode_normal); + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); } if (error) { @@ -470,6 +493,8 @@ void DynamicFontAtSize::_update_char(CharType p_char) { //find a texture to fit this... + int color_size = slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2; + Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; int tex_index = -1; int tex_x = 0; int tex_y = 0; @@ -478,6 +503,9 @@ void DynamicFontAtSize::_update_char(CharType p_char) { CharTexture &ct = textures[i]; + if (ct.texture->get_format() != require_format) + continue; + if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture continue; @@ -527,13 +555,13 @@ void DynamicFontAtSize::_update_char(CharType p_char) { CharTexture tex; tex.texture_size = texsize; - tex.imgdata.resize(texsize * texsize * 2); //grayscale alpha + tex.imgdata.resize(texsize * texsize * color_size); { //zero texture PoolVector<uint8_t>::Write w = tex.imgdata.write(); - ERR_FAIL_COND(texsize * texsize * 2 > tex.imgdata.size()); - for (int i = 0; i < texsize * texsize * 2; i++) { + ERR_FAIL_COND(texsize * texsize * color_size > tex.imgdata.size()); + for (int i = 0; i < texsize * texsize * color_size; i++) { w[i] = 0; } } @@ -555,7 +583,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) { for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { - int ofs = ((i + tex_y + rect_margin) * tex.texture_size + j + tex_x + rect_margin) * 2; + int ofs = ((i + tex_y + rect_margin) * tex.texture_size + j + tex_x + rect_margin) * color_size; ERR_FAIL_COND(ofs >= tex.imgdata.size()); switch (slot->bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: { @@ -568,7 +596,14 @@ void DynamicFontAtSize::_update_char(CharType p_char) { wr[ofs + 0] = 255; //grayscale as 1 wr[ofs + 1] = slot->bitmap.buffer[i * slot->bitmap.pitch + j]; break; - // TODO: FT_PIXEL_MODE_LCD, FT_PIXEL_MODE_BGRA + case FT_PIXEL_MODE_BGRA: { + int ofs_color = i * slot->bitmap.pitch + (j << 2); + wr[ofs + 2] = slot->bitmap.buffer[ofs_color + 0]; + wr[ofs + 1] = slot->bitmap.buffer[ofs_color + 1]; + wr[ofs + 0] = slot->bitmap.buffer[ofs_color + 2]; + wr[ofs + 3] = slot->bitmap.buffer[ofs_color + 3]; + } break; + // TODO: FT_PIXEL_MODE_LCD default: ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(slot->bitmap.pixel_mode)); ERR_FAIL(); @@ -581,7 +616,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) { //blit to image and texture { - Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, Image::FORMAT_LA8, tex.imgdata)); + Ref<Image> img = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata)); if (tex.texture.is_null()) { tex.texture.instance(); @@ -599,9 +634,9 @@ void DynamicFontAtSize::_update_char(CharType p_char) { } Character chr; - chr.h_align = xofs / oversampling; - chr.v_align = ascent - (yofs / oversampling); // + ascent - descent; - chr.advance = advance / oversampling; + chr.h_align = xofs * scale_color_font / oversampling; + chr.v_align = ascent - (yofs * scale_color_font / oversampling); // + ascent - descent; + chr.advance = advance * scale_color_font / oversampling; chr.texture_idx = tex_index; chr.found = true; @@ -640,6 +675,7 @@ DynamicFontAtSize::DynamicFontAtSize() { linegap = 1; texture_flags = 0; oversampling = font_oversampling; + scale_color_font = 1; } DynamicFontAtSize::~DynamicFontAtSize() { diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index 92bb77bed3..d8adf35b6b 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -106,6 +106,7 @@ class DynamicFontAtSize : public Reference { float linegap; float rect_margin; float oversampling; + float scale_color_font; uint32_t texture_flags; diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp index 78a19050f1..a9dec3e87c 100644 --- a/scene/resources/ray_shape.cpp +++ b/scene/resources/ray_shape.cpp @@ -43,7 +43,10 @@ Vector<Vector3> RayShape::_gen_debug_mesh_lines() { void RayShape::_update_shape() { - PhysicsServer::get_singleton()->shape_set_data(get_shape(), length); + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); emit_changed(); } @@ -52,6 +55,7 @@ void RayShape::set_length(float p_length) { length = p_length; _update_shape(); notify_change_to_owners(); + _change_notify("length"); } float RayShape::get_length() const { @@ -59,16 +63,33 @@ float RayShape::get_length() const { return length; } +void RayShape::set_slips_on_slope(bool p_active) { + + slips_on_slope = p_active; + _update_shape(); + notify_change_to_owners(); + _change_notify("slips_on_slope"); +} + +bool RayShape::get_slips_on_slope() const { + return slips_on_slope; +} + void RayShape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape::set_length); ClassDB::bind_method(D_METHOD("get_length"), &RayShape::get_length); + ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape::set_slips_on_slope); + ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape::get_slips_on_slope); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); } RayShape::RayShape() : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) { set_length(1.0); + set_slips_on_slope(false); } diff --git a/scene/resources/ray_shape.h b/scene/resources/ray_shape.h index 4bd96116fe..f11b2e7c3c 100644 --- a/scene/resources/ray_shape.h +++ b/scene/resources/ray_shape.h @@ -36,6 +36,7 @@ class RayShape : public Shape { GDCLASS(RayShape, Shape); float length; + bool slips_on_slope; protected: static void _bind_methods(); @@ -46,6 +47,9 @@ public: void set_length(float p_length); float get_length() const; + void set_slips_on_slope(bool p_active); + bool get_slips_on_slope() const; + RayShape(); }; #endif // RAY_SHAPE_H diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp index 81110015b5..58027c127d 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -106,7 +106,10 @@ SegmentShape2D::SegmentShape2D() : void RayShape2D::_update_shape() { - Physics2DServer::get_singleton()->shape_set_data(get_rid(), length); + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + Physics2DServer::get_singleton()->shape_set_data(get_rid(), d); emit_changed(); } @@ -140,7 +143,11 @@ void RayShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_length", "length"), &RayShape2D::set_length); ClassDB::bind_method(D_METHOD("get_length"), &RayShape2D::get_length); + ClassDB::bind_method(D_METHOD("set_slips_on_slope", "active"), &RayShape2D::set_slips_on_slope); + ClassDB::bind_method(D_METHOD("get_slips_on_slope"), &RayShape2D::get_slips_on_slope); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length"), "set_length", "get_length"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slips_on_slope"), "set_slips_on_slope", "get_slips_on_slope"); } void RayShape2D::set_length(real_t p_length) { @@ -153,9 +160,20 @@ real_t RayShape2D::get_length() const { return length; } +void RayShape2D::set_slips_on_slope(bool p_active) { + + slips_on_slope = p_active; + _update_shape(); +} + +bool RayShape2D::get_slips_on_slope() const { + return slips_on_slope; +} + RayShape2D::RayShape2D() : Shape2D(Physics2DServer::get_singleton()->ray_shape_create()) { length = 20; + slips_on_slope = false; _update_shape(); } diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h index 4ed30c0443..700982ac0a 100644 --- a/scene/resources/segment_shape_2d.h +++ b/scene/resources/segment_shape_2d.h @@ -63,6 +63,7 @@ class RayShape2D : public Shape2D { GDCLASS(RayShape2D, Shape2D); real_t length; + bool slips_on_slope; void _update_shape(); @@ -72,6 +73,10 @@ protected: public: void set_length(real_t p_length); real_t get_length() const; + + void set_slips_on_slope(bool p_active); + bool get_slips_on_slope() const; + virtual void draw(const RID &p_to_rid, const Color &p_color); virtual Rect2 get_rect() const; diff --git a/servers/physics/body_sw.h b/servers/physics/body_sw.h index e8ea5531e5..fd2ab16b84 100644 --- a/servers/physics/body_sw.h +++ b/servers/physics/body_sw.h @@ -245,12 +245,21 @@ public: biased_angular_velocity += _inv_inertia_tensor.xform(p_j); } + _FORCE_INLINE_ void add_central_force(const Vector3 &p_force) { + + applied_force += p_force; + } + _FORCE_INLINE_ void add_force(const Vector3 &p_force, const Vector3 &p_pos) { applied_force += p_force; applied_torque += p_pos.cross(p_force); } + _FORCE_INLINE_ void add_torque(const Vector3 &p_torque) { + applied_torque += p_torque; + } + void set_active(bool p_active); _FORCE_INLINE_ bool is_active() const { return active; } @@ -401,7 +410,9 @@ public: virtual void set_transform(const Transform &p_transform) { body->set_state(PhysicsServer::BODY_STATE_TRANSFORM, p_transform); } virtual Transform get_transform() const { return body->get_transform(); } + virtual void add_central_force(const Vector3 &p_force) { body->add_central_force(p_force); } virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos) { body->add_force(p_force, p_pos); } + virtual void add_torque(const Vector3 &p_torque) { body->add_torque(p_torque); } virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) { body->apply_impulse(p_pos, p_j); } virtual void apply_torque_impulse(const Vector3 &p_j) { body->apply_torque_impulse(p_j); } diff --git a/servers/physics/collision_solver_sw.cpp b/servers/physics/collision_solver_sw.cpp index e26a7a4d89..0037b9a862 100644 --- a/servers/physics/collision_solver_sw.cpp +++ b/servers/physics/collision_solver_sw.cpp @@ -90,6 +90,10 @@ bool CollisionSolverSW::solve_ray(const ShapeSW *p_shape_A, const Transform &p_t return false; Vector3 support_B = p_transform_B.xform(p); + if (ray->get_slips_on_slope()) { + Vector3 global_n = ai.basis.xform_inv(n).normalized(); + support_B = support_A + (support_B - support_A).length() * global_n; + } if (p_result_callback) { if (p_swap_result) diff --git a/servers/physics/shape_sw.cpp b/servers/physics/shape_sw.cpp index b918a42ca5..9d2e5e846d 100644 --- a/servers/physics/shape_sw.cpp +++ b/servers/physics/shape_sw.cpp @@ -165,6 +165,10 @@ real_t RayShapeSW::get_length() const { return length; } +bool RayShapeSW::get_slips_on_slope() const { + return slips_on_slope; +} + void RayShapeSW::project_range(const Vector3 &p_normal, const Transform &p_transform, real_t &r_min, real_t &r_max) const { // don't think this will be even used @@ -221,25 +225,31 @@ Vector3 RayShapeSW::get_moment_of_inertia(real_t p_mass) const { return Vector3(); } -void RayShapeSW::_setup(real_t p_length) { +void RayShapeSW::_setup(real_t p_length, bool p_slips_on_slope) { length = p_length; + slips_on_slope = p_slips_on_slope; configure(AABB(Vector3(0, 0, 0), Vector3(0.1, 0.1, length))); } void RayShapeSW::set_data(const Variant &p_data) { - _setup(p_data); + Dictionary d = p_data; + _setup(d["length"], d["slips_on_slope"]); } Variant RayShapeSW::get_data() const { - return length; + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + return d; } RayShapeSW::RayShapeSW() { length = 1; + slips_on_slope = false; } /********** SPHERE *************/ diff --git a/servers/physics/shape_sw.h b/servers/physics/shape_sw.h index 4a9a6289ff..7f7f9f4f98 100644 --- a/servers/physics/shape_sw.h +++ b/servers/physics/shape_sw.h @@ -149,11 +149,13 @@ public: class RayShapeSW : public ShapeSW { real_t length; + bool slips_on_slope; - void _setup(real_t p_length); + void _setup(real_t p_length, bool p_slips_on_slope); public: real_t get_length() const; + bool get_slips_on_slope() const; virtual real_t get_area() const { return 0.0; } virtual PhysicsServer::ShapeType get_type() const { return PhysicsServer::SHAPE_RAY; } diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp index 80cdd58aeb..ce06aa9a2b 100644 --- a/servers/physics_2d/collision_object_2d_sw.cpp +++ b/servers/physics_2d/collision_object_2d_sw.cpp @@ -100,6 +100,7 @@ void CollisionObject2DSW::remove_shape(int p_index) { shapes[p_index].shape->remove_owner(this); shapes.remove(p_index); + _update_shapes(); _shapes_changed(); } diff --git a/servers/physics_2d/collision_solver_2d_sw.cpp b/servers/physics_2d/collision_solver_2d_sw.cpp index a6ef110149..efee98a35a 100644 --- a/servers/physics_2d/collision_solver_2d_sw.cpp +++ b/servers/physics_2d/collision_solver_2d_sw.cpp @@ -95,6 +95,10 @@ bool CollisionSolver2DSW::solve_raycast(const Shape2DSW *p_shape_A, const Transf } Vector2 support_B = p_transform_B.xform(p); + if (ray->get_slips_on_slope()) { + Vector2 global_n = invb.basis_xform_inv(n).normalized(); + support_B = support_A + (support_B - support_A).length() * global_n; + } if (p_result_callback) { if (p_swap_result) diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp index 5893f19827..433942708e 100644 --- a/servers/physics_2d/shape_2d_sw.cpp +++ b/servers/physics_2d/shape_2d_sw.cpp @@ -184,13 +184,18 @@ real_t RayShape2DSW::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) void RayShape2DSW::set_data(const Variant &p_data) { - length = p_data; + Dictionary d = p_data; + length = d["length"]; + slips_on_slope = d["slips_on_slope"]; configure(Rect2(0, 0, 0.001, length)); } Variant RayShape2DSW::get_data() const { - return length; + Dictionary d; + d["length"] = length; + d["slips_on_slope"] = slips_on_slope; + return d; } /*********************************************************/ diff --git a/servers/physics_2d/shape_2d_sw.h b/servers/physics_2d/shape_2d_sw.h index c4c267b368..d937301f3c 100644 --- a/servers/physics_2d/shape_2d_sw.h +++ b/servers/physics_2d/shape_2d_sw.h @@ -197,9 +197,11 @@ public: class RayShape2DSW : public Shape2DSW { real_t length; + bool slips_on_slope; public: _FORCE_INLINE_ real_t get_length() const { return length; } + _FORCE_INLINE_ bool get_slips_on_slope() const { return slips_on_slope; } virtual Physics2DServer::ShapeType get_type() const { return Physics2DServer::SHAPE_RAY; } diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index 9d4807fcf0..db5e14043c 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -92,7 +92,9 @@ void PhysicsDirectBodyState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "transform"), &PhysicsDirectBodyState::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &PhysicsDirectBodyState::get_transform); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &PhysicsDirectBodyState::add_central_force); ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &PhysicsDirectBodyState::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &PhysicsDirectBodyState::add_torque); ClassDB::bind_method(D_METHOD("apply_impulse", "position", "j"), &PhysicsDirectBodyState::apply_impulse); ClassDB::bind_method(D_METHOD("apply_torqe_impulse", "j"), &PhysicsDirectBodyState::apply_torque_impulse); diff --git a/servers/physics_server.h b/servers/physics_server.h index 2ac405293e..c21aa32f6c 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -63,7 +63,9 @@ public: virtual void set_transform(const Transform &p_transform) = 0; virtual Transform get_transform() const = 0; + virtual void add_central_force(const Vector3 &p_force) = 0; virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos) = 0; + virtual void add_torque(const Vector3 &p_torque) = 0; virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) = 0; virtual void apply_torque_impulse(const Vector3 &p_j) = 0; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 29c27eee85..d9f2c949e9 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -1374,6 +1374,17 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID } }, //builtins - trigonometry + + { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "radians", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "radians", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "radians", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + + { "degrees", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "degrees", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "degrees", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "degrees", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + { "sin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, { "sin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, { "sin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, @@ -1423,6 +1434,21 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "tanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, { "tanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + { "asinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "asinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "asinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "asinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + + { "acosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "acosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "acosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "acosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + + { "atanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "atanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "atanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "atanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + //builtins - exponential { "pow", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "pow", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, @@ -1436,6 +1462,14 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "log", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, { "log", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, { "log", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + { "exp2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "exp2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "exp2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "exp2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + { "log2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "log2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "log2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "log2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, { "sqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, { "sqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, { "sqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, @@ -1482,6 +1516,10 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "round", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, { "round", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, { "round", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, + { "roundEven", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, + { "roundEven", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, + { "roundEven", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, + { "roundEven", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID } }, { "ceil", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, { "ceil", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } }, { "ceil", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } }, |