diff options
56 files changed, 1672 insertions, 293 deletions
diff --git a/AUTHORS.md b/AUTHORS.md index 430bb2dcd4..523f253228 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -57,6 +57,7 @@ name is available. Dharkael (lupoDharkael) Dmitry Koteroff (Krakean) DualMatrix + Emmanuel Barroga (sparkart) Emmanuel Leblond (touilleMan) Eric Lasota (elasota) est31 @@ -106,6 +107,7 @@ name is available. Masoud BH (masoudbh3) Matthias Hölzl (hoelzl) Max Hilbrunner (mhilbrunner) + merumelu Michael Alexsander Silva Dias (YeldhamDev) mrezai Nathan Warden (NathanWarden) @@ -17,6 +17,7 @@ generous deed immortalized in the next stable release of Godot Engine. ## Gold sponsors Gamblify <https://www.gamblify.com> + Moonwards <https://www.moonwards.com> ## Mini sponsors @@ -37,7 +38,9 @@ generous deed immortalized in the next stable release of Godot Engine. Hein-Pieter van Braam Jacob McKenney Javary Co. + Jeppe Zapp Justin Arnold + Justo Delgado Baudí Kyle Szklenski Leonard Meagher Matthieu Huvé @@ -57,6 +60,8 @@ generous deed immortalized in the next stable release of Godot Engine. Andrei cheese65536 + Daniel Hartmann + Dave David Gehrig Ed Morley Florian Krick @@ -70,14 +75,16 @@ generous deed immortalized in the next stable release of Godot Engine. Zaven Muradyan Alexander Trey Saunders - Allen Schade Asher Glick Austen McRae + beVR Brian van der Stel + Cameron MacNair Carlo Cabanilla Daniel James David Giardi David Snopek + Default Name Edward E Florian Breisch Gero @@ -85,15 +92,13 @@ generous deed immortalized in the next stable release of Godot Engine. Javier Roman Jay Horton Jonathan Turner - Jon Smith Jon Woodward - Justo Delgado Baudí + Jose Fernando Alexandre Karl Werf Kommentgames - Krzysztof Dluzniewski Luke Maciej Pendolski - Moonwards + Matthew Hillier Mored1984 Paul LaMotte Péter Magyar @@ -103,15 +108,19 @@ generous deed immortalized in the next stable release of Godot Engine. Sergey Shawn Yu Svenne Krap + thechris Tom Langwaldt + tukon William Wold Alex Khayrullin + Branwyn Tylwyth Chris Goddard Chris Serino Christian Padilla Conrad Curry Craig Smith + Darrian Little Dean Harmon Ian Richard Kunert Ivan Trombley @@ -120,8 +129,8 @@ generous deed immortalized in the next stable release of Godot Engine. Krzysztof Jankowski Lord Bloodhound Lucas Ferreira Franca - Michele Zilli Nathan Lundquist + Nicklas Breum Pascal Grüter Petr Malac Rami @@ -139,6 +148,8 @@ generous deed immortalized in the next stable release of Godot Engine. Adam Neumann Alexander J Maynard Alexey Dyadchenko + André Frélicot + andres eduardo lopez Andrew Bowen Asdf Ben Botwin @@ -148,14 +159,13 @@ generous deed immortalized in the next stable release of Godot Engine. Christoph Schröder Cody Parker D - Daniel Daniel Eichler David White Eric + Eric Churches Eric Monson Eugenio Hugo Salgüero Jáñez flesk - Francisco Javier Moreno Carracedo gavlig GGGames.org Guilherme Felipe de C. G. da Silva @@ -163,32 +173,34 @@ generous deed immortalized in the next stable release of Godot Engine. Hysteria Idzard Kwadijk Jared White - Jesse Nave + Joe Flood Jose Malheiro Joshua Lesperance Juan T Chen Juraj Móza Kasper Jeppesen + kinfox Klaus The. Klavdij Voncina Maarten Elings + Marcelo Dornbusch Lopes Markus Fehr Markus Wiesner Martin Eigel Marvin Matt Eunson - Matthew Hillier Max Bulai Max R.R. Collada M H Nick Nikitin Oliver Dick - Paolo Munoz + Patrick Ting Paul Hocker Paul Von Zimmerman Pete Goodwin pl Ranoller + Romildo Franco Samuel Judd Scott Pilet spilldata @@ -196,7 +208,7 @@ generous deed immortalized in the next stable release of Godot Engine. Thomas Krampl Tobias Bocanegra Urho - Xavier Fumado Beltran + Zie Weaver ## Silver donors @@ -209,6 +221,7 @@ generous deed immortalized in the next stable release of Godot Engine. Adam Smeltzer Adisibio Adrian Demetrescu + Aggelos Arnaoutis Agustinus Arya Aidan O'Flannagain Albin Jonasson Svärdsby @@ -216,22 +229,25 @@ generous deed immortalized in the next stable release of Godot Engine. Alessandro Senese Alexander Koppe Alex Davies-Moore + Allen Schade Andreas Evers Andreas Krampitz Andreas Lundmark Andreas Schüle + André Simões Andrés Rodríguez Andrzej Skalski Anthony Bongiovanni Anthony Staunton + Anton Kurkin Antony K. Jones + AP Condomines Arda Erol Arthur S. Muszynski Aubrey Falconer Avencherus B A Balázs Batári - Bastian Böhm Beliar Benedikt Ben Phelan @@ -240,14 +256,11 @@ generous deed immortalized in the next stable release of Godot Engine. Black Block Blair Allen Bobby CC Wong - Boyquotes - Branwyn Tylwyth Bryan Stevenson Caleb Dumitry Carwyn Edwards Chris Brown Chris Chapin - Chris Gonzales Christian Baune Christian Winter Christoffer Sundbom @@ -256,18 +269,22 @@ generous deed immortalized in the next stable release of Godot Engine. Cobaltum Collin Shooltz Dag Sundin Söderström + Dan H. Bentsen Daniel Johnson DanielMaximiano + Daniel Pontillo Daniel Reed Daniel Tebbutt David Bullock David Cravens David May - Dimitri Stanojevic + David Rapisarda + David Woodard Dominic Cooney Dominik Wetzel - DrevanTonder + Donovan Hutcheon Duobix + Eduardo Teixeira Edward Herbert Egon Elbre Ellen Marie Dash @@ -276,18 +293,21 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Ellingson Eric Martini Eric Williams + EugeneTel Evan Rose Felix Kollmann fengjiongmax Flaredown FuDiggity G3Dev sàrl + Gadzhi Kharkharov + gamedev by Celio Gary Hulst - Gerrit Großkopf + George Marques gmmath - Grant Clarke Greg Olson Greg P + Greyson Richey Guldoman Hal A Heribert Hirth @@ -295,7 +315,6 @@ generous deed immortalized in the next stable release of Godot Engine. Hunter Jones Hylpher ialex32x - Igor Buzatovic Iiari IndustrialRobot Isaac Morton @@ -322,6 +341,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jon Bonazza Jon Sully Jose Aleman + Jose Andrés Mejias Rojas Joseph Catrambone Josh 'Cheeseness' Bush Juanfran @@ -330,8 +350,10 @@ generous deed immortalized in the next stable release of Godot Engine. Judd Jueast Julian Murgia + Justin Spedding + Kaiser Bald0 Kamuna - Kasier Bald0 + Kauzig KC Chan Keedong Park kickmaniac @@ -346,7 +368,6 @@ generous deed immortalized in the next stable release of Godot Engine. Levi Lindsey Linus Lind Lundgren Lionel Gaillard - Luis Moraes LunaticInAHat Lurkars Macil @@ -354,11 +375,12 @@ generous deed immortalized in the next stable release of Godot Engine. Malcolm Malik Ahmed Malik Nejer - Marc Urlus Marcus Richter Markus Lohaus Markus Michael Egger Martin Holas + Martin Liška + Matt Edwards Matthew Little Maxwell medecau @@ -369,41 +391,44 @@ generous deed immortalized in the next stable release of Godot Engine. Michael Labbe Mikael Olsson Mikayla Hutchinson + Mike Birkhead Mike Cunningham Mitchell J. Wagner - mlevin cantu MoM MuffinManKen + Nathan Fish Natrim nee Neil Blakey-Milner Nerdforge Nicholas + Nicholas Bettencourt + Nick Macholl Niclas Eriksen Nicolás Montaña Nicolas SAN AGUSTIN Nima Farid Nithin Jino NZ + Olivier Omar Delarosa omzee Oscar Norlander Pafka Pan Ip + Pat LaBine Patrick Forringer Patrick Nafarrete Paul Gieske Paul Mason Paweł Kowal + Philip Cohoe Pierre-Igor Berthet - Pietro Vertechi Pitsanu Tongprasin Point08 Poryg - Rafael Delboni Rafa Laguna Rafal Wyszomirski - rainerLinux Raphael Leroux Remi Rampin Rémi Verschelde @@ -411,10 +436,12 @@ generous deed immortalized in the next stable release of Godot Engine. Ricardo Alcantara Robert Farr (Larington) Robert Hernandez + Robert Larnach Rodrigo Loli Roger Smith Roland Rząsa Roman Tinkov + Ronan Jouchet Ryan Ryan Brooks Ryan Groom @@ -426,13 +453,13 @@ generous deed immortalized in the next stable release of Godot Engine. Scott D. Yelich Sebastian Michailidis sgnsajgon + Shane Shane Sicienski Shane Spoor Simon Ledam Simon Wenner SK Sootstone - Stonepyre Taylor Fahlman thomas Thomas Bell @@ -443,10 +470,14 @@ generous deed immortalized in the next stable release of Godot Engine. Tim Gudex Timothy B. MacDonald Tobbun + Tom Fulp + Tom Glenn Tom Larrow Torsten Crass Travis O'Brien Trent Skinner + Triptych + Troy Bonneau Tryggve Sollid Turgut Temucin Tyler Stafos @@ -454,7 +485,9 @@ generous deed immortalized in the next stable release of Godot Engine. Vaiktorg Victor Vigilant Watch + Vincent Cloutier waka nya + Walter Byers Wayne Haak werner mendizabal Wiley Thompson @@ -463,6 +496,7 @@ generous deed immortalized in the next stable release of Godot Engine. Wout Standaert Wyatt Goodin Yegor + 蕭惟允 ## Bronze donors diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index af845ca01e..9078abea68 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -300,6 +300,11 @@ public: } static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. real_t tolerance = CMP_EPSILON * abs(a); if (tolerance < CMP_EPSILON) { tolerance = CMP_EPSILON; @@ -308,6 +313,11 @@ public: } static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. return abs(a - b) < tolerance; } diff --git a/core/math/transform.h b/core/math/transform.h index 1fdc6398a1..90e2b07583 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -158,22 +158,29 @@ _FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const { } _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const { - /* define vertices */ - Vector3 x = basis.get_axis(0) * p_aabb.size.x; - Vector3 y = basis.get_axis(1) * p_aabb.size.y; - Vector3 z = basis.get_axis(2) * p_aabb.size.z; - Vector3 pos = xform(p_aabb.position); - //could be even further optimized - AABB new_aabb; - new_aabb.position = pos; - new_aabb.expand_to(pos + x); - new_aabb.expand_to(pos + y); - new_aabb.expand_to(pos + z); - new_aabb.expand_to(pos + x + y); - new_aabb.expand_to(pos + x + z); - new_aabb.expand_to(pos + y + z); - new_aabb.expand_to(pos + x + y + z); - return new_aabb; + + /* http://dev.theomader.com/transform-bounding-boxes/ */ + Vector3 min = p_aabb.position; + Vector3 max = p_aabb.position + p_aabb.size; + Vector3 tmin, tmax; + for (int i = 0; i < 3; i++) { + tmin[i] = tmax[i] = origin[i]; + for (int j = 0; j < 3; j++) { + real_t e = basis[i][j] * min[j]; + real_t f = basis[i][j] * max[j]; + if (e < f) { + tmin[i] += e; + tmax[i] += f; + } else { + tmin[i] += f; + tmax[i] += e; + } + } + } + AABB r_aabb; + r_aabb.position = tmin; + r_aabb.size = tmax - tmin; + return r_aabb; } _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const { diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index a40a50cfce..30fca0c155 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -450,7 +450,7 @@ bool InputEventMouseButton::is_doubleclick() const { Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Vector2 g = p_xform.xform(get_global_position()); + Vector2 g = get_global_position(); Vector2 l = p_xform.xform(get_position() + p_local_ofs); Ref<InputEventMouseButton> mb; @@ -577,7 +577,7 @@ Vector2 InputEventMouseMotion::get_speed() const { Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { - Vector2 g = p_xform.xform(get_global_position()); + Vector2 g = get_global_position(); Vector2 l = p_xform.xform(get_position() + p_local_ofs); Vector2 r = p_xform.basis_xform(get_relative()); Vector2 s = p_xform.basis_xform(get_speed()); diff --git a/core/script_language.cpp b/core/script_language.cpp index 97758ced66..ee8589d76a 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -50,6 +50,52 @@ void Script::_notification(int p_what) { } } +Variant Script::_get_property_default_value(const StringName &p_property) { + Variant ret; + get_property_default_value(p_property, ret); + return ret; +} + +Array Script::_get_script_property_list() { + Array ret; + List<PropertyInfo> list; + get_script_property_list(&list); + for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { + ret.append(E->get().operator Dictionary()); + } + return ret; +} + +Array Script::_get_script_method_list() { + Array ret; + List<MethodInfo> list; + get_script_method_list(&list); + for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { + ret.append(E->get().operator Dictionary()); + } + return ret; +} + +Array Script::_get_script_signal_list() { + Array ret; + List<MethodInfo> list; + get_script_signal_list(&list); + for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { + ret.append(E->get().operator Dictionary()); + } + return ret; +} + +Dictionary Script::_get_script_constant_map() { + Dictionary ret; + Map<StringName, Variant> map; + get_constants(&map); + for (Map<StringName, Variant>::Element *E = map.front(); E; E = E->next()) { + ret[E->key()] = E->value(); + } + return ret; +} + void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("can_instance"), &Script::can_instance); @@ -64,6 +110,12 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal); + ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list); + ClassDB::bind_method(D_METHOD("get_script_method_list"), &Script::_get_script_method_list); + ClassDB::bind_method(D_METHOD("get_script_signal_list"), &Script::_get_script_signal_list); + ClassDB::bind_method(D_METHOD("get_script_constant_map"), &Script::_get_script_constant_map); + ClassDB::bind_method(D_METHOD("get_property_default_value"), &Script::_get_property_default_value); + ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool); ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", 0), "set_source_code", "get_source_code"); diff --git a/core/script_language.h b/core/script_language.h index dfb2e0ad31..1b4f1eb4cd 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -109,6 +109,12 @@ protected: friend class PlaceHolderScriptInstance; virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {} + Variant _get_property_default_value(const StringName &p_property); + Array _get_script_property_list(); + Array _get_script_method_list(); + Array _get_script_signal_list(); + Dictionary _get_script_constant_map(); + public: virtual bool can_instance() const = 0; diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 6a8cdcd2a8..305be8b58d 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -17,6 +17,9 @@ <member name="clip_text" type="bool" setter="set_clip_text" getter="get_clip_text" default="false"> When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text. </member> + <member name="expand_icon" type="bool" setter="set_expand_icon" getter="is_expand_icon" default="false"> + When enabled, the button's icon will expand/shrink to fit the button's size while keeping its aspect. + </member> <member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false"> Flat buttons don't display decoration. </member> diff --git a/doc/classes/EditorVCSInterface.xml b/doc/classes/EditorVCSInterface.xml new file mode 100644 index 0000000000..2f58e6d54e --- /dev/null +++ b/doc/classes/EditorVCSInterface.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="EditorVCSInterface" inherits="Object" category="Core" version="3.2"> + <brief_description> + Version Control System (VCS) interface which reads and writes to the local VCS in use. + </brief_description> + <description> + Used by the editor to display VCS extracted information in the editor. The implementation of this API is included in VCS addons, which are essentially GDNative plugins that need to be put into the project folder. These VCS addons are scripts which are attached (on demand) to the object instance of [code]EditorVCSInterface[/code]. All the functions listed below, instead of performing the task themselves, they call the internally defined functions in the VCS addons to provide a plug-n-play experience. + </description> + <tutorials> + </tutorials> + <methods> + <method name="commit"> + <return type="void"> + </return> + <argument index="0" name="msg" type="String"> + </argument> + <description> + Creates a version commit if the addon is initialized, else returns without doing anything. Uses the files which have been staged previously, with the commit message set to a value as provided as in the argument. + </description> + </method> + <method name="get_file_diff"> + <return type="Array"> + </return> + <argument index="0" name="file_path" type="String"> + </argument> + <description> + Returns an [Array] of [Dictionary] objects containing the diff output from the VCS in use, if a VCS addon is initialized, else returns an empty [Array] object. The diff contents also consist of some contextual lines which provide context to the observed line change in the file. + Each [Dictionary] object has the line diff contents under the keys: + - [code]"content"[/code] to store a [String] containing the line contents + - [code]"status"[/code] to store a [String] which contains [code]"+"[/code] in case the content is a line addition but it stores a [code]"-"[/code] in case of deletion and an empty string in the case the line content is neither an addition nor a deletion. + - [code]"new_line_number"[/code] to store an integer containing the new line number of the line content. + - [code]"line_count"[/code] to store an integer containing the number of lines in the line content. + - [code]"old_line_number"[/code] to store an integer containing the old line number of the line content. + - [code]"offset"[/code] to store the offset of the line change since the first contextual line content. + </description> + </method> + <method name="get_is_vcs_intialized"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if the VCS addon has been intialized, else returns [code]false[/code]. + </description> + </method> + <method name="get_modified_files_data"> + <return type="Dictionary"> + </return> + <description> + Returns a [Dictionary] containing the path of the detected file change mapped to an integer signifying what kind of a change the corresponding file has experienced. + The following integer values are being used to signify that the detected file is: + - [code]0[/code]: New to the VCS working directory + - [code]1[/code]: Modified + - [code]2[/code]: Renamed + - [code]3[/code]: Deleted + - [code]4[/code]: Typechanged + </description> + </method> + <method name="get_project_name"> + <return type="String"> + </return> + <description> + Return the project name of the VCS working directory + </description> + </method> + <method name="get_vcs_name"> + <return type="String"> + </return> + <description> + Return the name of the VCS if the VCS has been intialized, else return an empty string. + </description> + </method> + <method name="initialize"> + <return type="bool"> + </return> + <argument index="0" name="project_root_path" type="String"> + </argument> + <description> + Initialize the VCS addon if not already. Uses the argument value as the path to the working directory of the project. Creates the initial commit if required. Returns [code]true[/code] if no failure occurs, else returns [code]false[/code]. + </description> + </method> + <method name="is_addon_ready"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if the addon is ready to respond to function calls, else returns [code]false[/code]. + </description> + </method> + <method name="shut_down"> + <return type="bool"> + </return> + <description> + Shuts down the VCS addon to allow cleanup code to run on call. Returns [code]true[/code] is no failure occurs, else returns [code]false[/code]. + </description> + </method> + <method name="stage_file"> + <return type="void"> + </return> + <argument index="0" name="file_path" type="String"> + </argument> + <description> + Stage the file which should be committed when [method EditorVCSInterface.commit] is called. Argument should contain the absolute path. + </description> + </method> + <method name="unstage_file"> + <return type="void"> + </return> + <argument index="0" name="file_path" type="String"> + </argument> + <description> + Unstage the file which was staged previously to be committed, so that it is no longer committed when [method EditorVCSInterface.commit] is called. Argument should contain the absolute path. + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml index 8aefa41f8a..8470a346ff 100644 --- a/doc/classes/GraphNode.xml +++ b/doc/classes/GraphNode.xml @@ -174,6 +174,7 @@ </methods> <members> <member name="comment" type="bool" setter="set_comment" getter="is_comment" default="false"> + If [code]true[/code], the GraphNode is a comment node. </member> <member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2( 0, 0 )"> The offset of the GraphNode, relative to the scroll offset of the [GraphEdit]. diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 14ea18f885..6728f60e06 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -354,10 +354,12 @@ void AnimationBezierTrackEdit::_notification(int p_what) { { //guides float min_left_scale = font->get_height() + vsep; - float scale = 1; + float scale = (min_left_scale * 2) * v_zoom; + float step = Math::pow(10.0, Math::round(Math::log(scale / 5.0) / Math::log(10.0))) * 5.0; + scale = Math::stepify(scale, step); while (scale / v_zoom < min_left_scale * 2) { - scale *= 5; + scale += step; } bool first = true; @@ -378,7 +380,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { draw_line(Point2(limit, i), Point2(right_limit, i), lc); Color c = color; c.a *= 0.5; - draw_string(font, Point2(limit + 8, i - 2), itos((iv + 1) * scale), c); + draw_string(font, Point2(limit + 8, i - 2), rtos(Math::stepify((iv + 1) * scale, step)), c); } first = false; @@ -628,24 +630,28 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { + float v_zoom_orig = v_zoom; if (mb->get_command()) { timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); } else { - if (v_zoom < 1000) { + if (v_zoom < 100000) { v_zoom *= 1.2; } } + v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig); update(); } if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { + float v_zoom_orig = v_zoom; if (mb->get_command()) { timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); } else { - if (v_zoom > 0.01) { + if (v_zoom > 0.000001) { v_zoom /= 1.2; } } + v_scroll = v_scroll + (mb->get_position().y - get_size().y / 2) * (v_zoom - v_zoom_orig); update(); } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8ed6cec779..aab890be00 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -122,6 +122,7 @@ #include "editor/plugins/theme_editor_plugin.h" #include "editor/plugins/tile_map_editor_plugin.h" #include "editor/plugins/tile_set_editor_plugin.h" +#include "editor/plugins/version_control_editor_plugin.h" #include "editor/plugins/visual_shader_editor_plugin.h" #include "editor/pvrtc_compress.h" #include "editor/register_exporters.h" @@ -184,6 +185,20 @@ void EditorNode::_update_scene_tabs() { } } +void EditorNode::_version_control_menu_option(int p_idx) { + + switch (vcs_actions_menu->get_item_id(p_idx)) { + case RUN_VCS_SETTINGS: { + + VersionControlEditorPlugin::get_singleton()->popup_vcs_set_up_dialog(gui_base); + } break; + case RUN_VCS_SHUT_DOWN: { + + VersionControlEditorPlugin::get_singleton()->shut_down(); + } break; + } +} + void EditorNode::_update_title() { String appname = ProjectSettings::get_singleton()->get("application/config/name"); @@ -3523,6 +3538,7 @@ void EditorNode::register_editor_types() { ClassDB::register_class<EditorResourcePreviewGenerator>(); ClassDB::register_virtual_class<EditorFileSystem>(); ClassDB::register_class<EditorFileSystemDirectory>(); + ClassDB::register_class<EditorVCSInterface>(); ClassDB::register_virtual_class<ScriptEditor>(); ClassDB::register_virtual_class<EditorInterface>(); ClassDB::register_class<EditorExportPlugin>(); @@ -5312,6 +5328,7 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("_dropped_files", &EditorNode::_dropped_files); ClassDB::bind_method(D_METHOD("_global_menu_action"), &EditorNode::_global_menu_action, DEFVAL(Variant())); ClassDB::bind_method("_toggle_distraction_free_mode", &EditorNode::_toggle_distraction_free_mode); + ClassDB::bind_method("_version_control_menu_option", &EditorNode::_version_control_menu_option); ClassDB::bind_method("edit_item_resource", &EditorNode::edit_item_resource); ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base); @@ -5637,6 +5654,8 @@ EditorNode::EditorNode() { EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "SpatialMaterial,Script,MeshLibrary,TileSet"); + EDITOR_DEF("interface/inspector/default_color_picker_mode", 0); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW", PROPERTY_USAGE_DEFAULT)); EDITOR_DEF("run/auto_save/save_before_running", true); theme_base = memnew(Control); @@ -5996,6 +6015,15 @@ EditorNode::EditorNode() { p->add_shortcut(ED_SHORTCUT("editor/project_settings", TTR("Project Settings...")), RUN_SETTINGS); p->connect("id_pressed", this, "_menu_option"); + vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel(); + vcs_actions_menu->set_name("Version Control"); + vcs_actions_menu->connect("index_pressed", this, "_version_control_menu_option"); + p->add_separator(); + p->add_child(vcs_actions_menu); + p->add_submenu_item(TTR("Version Control"), "Version Control"); + vcs_actions_menu->add_item(TTR("Set Up Version Control"), RUN_VCS_SETTINGS); + vcs_actions_menu->add_item(TTR("Shut Down Version Control"), RUN_VCS_SHUT_DOWN); + p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/export", TTR("Export...")), FILE_EXPORT_PROJECT); p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE); @@ -6005,7 +6033,6 @@ EditorNode::EditorNode() { plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready"); gui_base->add_child(plugin_config_dialog); - p->add_separator(); tool_menu = memnew(PopupMenu); tool_menu->set_name("Tools"); tool_menu->connect("index_pressed", this, "_tool_menu_option"); @@ -6470,6 +6497,7 @@ EditorNode::EditorNode() { //more visually meaningful to have this later raise_bottom_panel_item(AnimationPlayerEditor::singleton); + add_editor_plugin(VersionControlEditorPlugin::get_singleton()); add_editor_plugin(memnew(ShaderEditorPlugin(this))); add_editor_plugin(memnew(VisualShaderEditorPlugin(this))); diff --git a/editor/editor_node.h b/editor/editor_node.h index 54bcf14c1b..5ecb472e64 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -178,8 +178,12 @@ private: RUN_DEBUG_NAVIGATION, RUN_DEPLOY_REMOTE_DEBUG, RUN_RELOAD_SCRIPTS, + RUN_VCS_SETTINGS, + RUN_VCS_SHUT_DOWN, SETTINGS_UPDATE_CONTINUOUSLY, SETTINGS_UPDATE_WHEN_CHANGED, + SETTINGS_UPDATE_ALWAYS, + SETTINGS_UPDATE_CHANGES, SETTINGS_UPDATE_SPINNER_HIDE, SETTINGS_PREFERENCES, SETTINGS_LAYOUT_SAVE, @@ -322,6 +326,7 @@ private: EditorSettingsDialog *settings_config_dialog; RunSettingsDialog *run_settings_dialog; ProjectSettingsEditor *project_settings; + PopupMenu *vcs_actions_menu; EditorFileDialog *file; ExportTemplateManager *export_template_manager; EditorFeatureProfileManager *feature_profile_manager; @@ -477,6 +482,7 @@ private: void _get_scene_metadata(const String &p_file); void _update_title(); void _update_scene_tabs(); + void _version_control_menu_option(int p_idx); void _close_messages(); void _show_messages(); void _vp_resized(); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index d9fd0659aa..310a107ca9 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -211,6 +211,10 @@ String EditorInterface::get_selected_path() const { return EditorNode::get_singleton()->get_filesystem_dock()->get_selected_path(); } +String EditorInterface::get_current_path() const { + return EditorNode::get_singleton()->get_filesystem_dock()->get_current_path(); +} + void EditorInterface::inspect_object(Object *p_obj, const String &p_for_property) { EditorNode::get_singleton()->push_item(p_obj, p_for_property); @@ -288,6 +292,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("make_mesh_previews", "meshes", "preview_size"), &EditorInterface::_make_mesh_previews); ClassDB::bind_method(D_METHOD("select_file", "file"), &EditorInterface::select_file); ClassDB::bind_method(D_METHOD("get_selected_path"), &EditorInterface::get_selected_path); + ClassDB::bind_method(D_METHOD("get_current_path"), &EditorInterface::get_current_path); ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled); ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 8941dfa28c..63f5a4f87a 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -78,6 +78,7 @@ public: void select_file(const String &p_file); String get_selected_path() const; + String get_current_path() const; void inspect_object(Object *p_obj, const String &p_for_property = String()); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index d7b3c7733b..30fb561fbe 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1842,10 +1842,20 @@ void EditorPropertyColor::_popup_closed() { emit_changed(get_edited_property(), picker->get_pick_color(), "", false); } +void EditorPropertyColor::_picker_created() { + // get default color picker mode from editor settings + int default_color_mode = EDITOR_GET("interface/inspector/default_color_picker_mode"); + if (default_color_mode == 1) + picker->get_picker()->set_hsv_mode(true); + else if (default_color_mode == 2) + picker->get_picker()->set_raw_mode(true); +} + void EditorPropertyColor::_bind_methods() { ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed); ClassDB::bind_method(D_METHOD("_popup_closed"), &EditorPropertyColor::_popup_closed); + ClassDB::bind_method(D_METHOD("_picker_created"), &EditorPropertyColor::_picker_created); } void EditorPropertyColor::update_property() { @@ -1864,6 +1874,7 @@ EditorPropertyColor::EditorPropertyColor() { picker->set_flat(true); picker->connect("color_changed", this, "_color_changed"); picker->connect("popup_closed", this, "_popup_closed"); + picker->connect("picker_created", this, "_picker_created"); } ////////////// NODE PATH ////////////////////// diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 0a4a07cdc0..adf7779dc4 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -493,6 +493,7 @@ class EditorPropertyColor : public EditorProperty { ColorPickerButton *picker; void _color_changed(const Color &p_color); void _popup_closed(); + void _picker_created(); protected: static void _bind_methods(); diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 46a30b7554..35fe366526 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -138,20 +138,39 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) { void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; + + if (grabbing_grabber) { + if (mb.is_valid()) { + + if (mb->get_button_index() == BUTTON_WHEEL_UP) { + set_value(get_value() + get_step()); + mousewheel_over_grabber = true; + } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN) { + set_value(get_value() - get_step()); + mousewheel_over_grabber = true; + } + } + } + if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) { if (mb->is_pressed()) { grabbing_grabber = true; - grabbing_ratio = get_as_ratio(); - grabbing_from = grabber->get_transform().xform(mb->get_position()).x; + if (!mousewheel_over_grabber) { + grabbing_ratio = get_as_ratio(); + grabbing_from = grabber->get_transform().xform(mb->get_position()).x; + } } else { grabbing_grabber = false; + mousewheel_over_grabber = false; } } Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid() && grabbing_grabber) { + if (mousewheel_over_grabber) + return; float grabbing_ofs = (grabber->get_transform().xform(mm->get_position()).x - grabbing_from) / float(grabber_range); set_as_ratio(grabbing_ratio + grabbing_ofs); @@ -267,6 +286,11 @@ void EditorSpinSlider::_notification(int p_what) { grabber->set_size(Size2(0, 0)); grabber->set_position(get_global_position() + grabber_rect.position + grabber_rect.size * 0.5 - grabber->get_size() * 0.5); + + if (mousewheel_over_grabber) { + Input::get_singleton()->warp_mouse_position(grabber->get_position() + grabber_rect.size); + } + grabber_range = width; } } @@ -462,6 +486,7 @@ EditorSpinSlider::EditorSpinSlider() { grabber->connect("gui_input", this, "_grabber_gui_input"); mouse_over_spin = false; mouse_over_grabber = false; + mousewheel_over_grabber = false; grabbing_grabber = false; grabber_range = 1; value_input = memnew(LineEdit); diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h index d91380e175..2dc6e2c2f2 100644 --- a/editor/editor_spin_slider.h +++ b/editor/editor_spin_slider.h @@ -48,6 +48,7 @@ class EditorSpinSlider : public Range { bool mouse_over_spin; bool mouse_over_grabber; + bool mousewheel_over_grabber; bool grabbing_grabber; int grabbing_from; diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp new file mode 100644 index 0000000000..db1c289ec9 --- /dev/null +++ b/editor/editor_vcs_interface.cpp @@ -0,0 +1,173 @@ +#include "editor_vcs_interface.h" + +EditorVCSInterface *EditorVCSInterface::singleton = NULL; + +void EditorVCSInterface::_bind_methods() { + + // Proxy end points that act as fallbacks to unavailability of a function in the VCS addon + ClassDB::bind_method(D_METHOD("_initialize", "project_root_path"), &EditorVCSInterface::_initialize); + ClassDB::bind_method(D_METHOD("_get_is_vcs_intialized"), &EditorVCSInterface::_get_is_vcs_intialized); + ClassDB::bind_method(D_METHOD("_get_vcs_name"), &EditorVCSInterface::_get_vcs_name); + ClassDB::bind_method(D_METHOD("_shut_down"), &EditorVCSInterface::_shut_down); + ClassDB::bind_method(D_METHOD("_get_project_name"), &EditorVCSInterface::_get_project_name); + ClassDB::bind_method(D_METHOD("_get_modified_files_data"), &EditorVCSInterface::_get_modified_files_data); + ClassDB::bind_method(D_METHOD("_commit", "msg"), &EditorVCSInterface::_commit); + ClassDB::bind_method(D_METHOD("_get_file_diff", "file_path"), &EditorVCSInterface::_get_file_diff); + ClassDB::bind_method(D_METHOD("_stage_file", "file_path"), &EditorVCSInterface::_stage_file); + ClassDB::bind_method(D_METHOD("_unstage_file", "file_path"), &EditorVCSInterface::_unstage_file); + + ClassDB::bind_method(D_METHOD("is_addon_ready"), &EditorVCSInterface::is_addon_ready); + + // API methods that redirect calls to the proxy end points + ClassDB::bind_method(D_METHOD("initialize", "project_root_path"), &EditorVCSInterface::initialize); + ClassDB::bind_method(D_METHOD("get_is_vcs_intialized"), &EditorVCSInterface::get_is_vcs_intialized); + ClassDB::bind_method(D_METHOD("get_modified_files_data"), &EditorVCSInterface::get_modified_files_data); + ClassDB::bind_method(D_METHOD("stage_file", "file_path"), &EditorVCSInterface::stage_file); + ClassDB::bind_method(D_METHOD("unstage_file", "file_path"), &EditorVCSInterface::unstage_file); + ClassDB::bind_method(D_METHOD("commit", "msg"), &EditorVCSInterface::commit); + ClassDB::bind_method(D_METHOD("get_file_diff", "file_path"), &EditorVCSInterface::get_file_diff); + ClassDB::bind_method(D_METHOD("shut_down"), &EditorVCSInterface::shut_down); + ClassDB::bind_method(D_METHOD("get_project_name"), &EditorVCSInterface::get_project_name); + ClassDB::bind_method(D_METHOD("get_vcs_name"), &EditorVCSInterface::get_vcs_name); +} + +bool EditorVCSInterface::_initialize(String p_project_root_path) { + + WARN_PRINT("Selected VCS addon does not implement an initialization function. This warning will be suppressed.") + return true; +} + +bool EditorVCSInterface::_get_is_vcs_intialized() { + + return false; +} + +Dictionary EditorVCSInterface::_get_modified_files_data() { + + return Dictionary(); +} + +void EditorVCSInterface::_stage_file(String p_file_path) { + + return; +} + +void EditorVCSInterface::_unstage_file(String p_file_path) { + + return; +} + +void EditorVCSInterface::_commit(String p_msg) { + + return; +} + +Array EditorVCSInterface::_get_file_diff(String p_file_path) { + + return Array(); +} + +bool EditorVCSInterface::_shut_down() { + + return false; +} + +String EditorVCSInterface::_get_project_name() { + + return String(); +} + +String EditorVCSInterface::_get_vcs_name() { + + return ""; +} + +bool EditorVCSInterface::initialize(String p_project_root_path) { + + is_initialized = call("_initialize", p_project_root_path); + return is_initialized; +} + +bool EditorVCSInterface::get_is_vcs_intialized() { + + return call("_get_is_vcs_intialized"); +} + +Dictionary EditorVCSInterface::get_modified_files_data() { + + return call("_get_modified_files_data"); +} + +void EditorVCSInterface::stage_file(String p_file_path) { + + if (is_addon_ready()) { + + call("_stage_file", p_file_path); + } + return; +} + +void EditorVCSInterface::unstage_file(String p_file_path) { + + if (is_addon_ready()) { + + call("_unstage_file", p_file_path); + } + return; +} + +bool EditorVCSInterface::is_addon_ready() { + + return is_initialized; +} + +void EditorVCSInterface::commit(String p_msg) { + + if (is_addon_ready()) { + + call("_commit", p_msg); + } + return; +} + +Array EditorVCSInterface::get_file_diff(String p_file_path) { + + if (is_addon_ready()) { + + return call("_get_file_diff", p_file_path); + } + return Array(); +} + +bool EditorVCSInterface::shut_down() { + + return call("_shut_down"); +} + +String EditorVCSInterface::get_project_name() { + + return call("_get_project_name"); +} + +String EditorVCSInterface::get_vcs_name() { + + return call("_get_vcs_name"); +} + +EditorVCSInterface::EditorVCSInterface() { + + is_initialized = false; +} + +EditorVCSInterface::~EditorVCSInterface() { +} + +EditorVCSInterface *EditorVCSInterface::get_singleton() { + + return singleton; +} + +void EditorVCSInterface::set_singleton(EditorVCSInterface *p_singleton) { + + singleton = p_singleton; +} diff --git a/editor/editor_vcs_interface.h b/editor/editor_vcs_interface.h new file mode 100644 index 0000000000..baf196de6e --- /dev/null +++ b/editor/editor_vcs_interface.h @@ -0,0 +1,53 @@ +#ifndef EDITOR_VCS_INTERFACE_H +#define EDITOR_VCS_INTERFACE_H + +#include "core/object.h" +#include "core/ustring.h" +#include "scene/gui/panel_container.h" + +class EditorVCSInterface : public Object { + + GDCLASS(EditorVCSInterface, Object) + + bool is_initialized; + +protected: + static EditorVCSInterface *singleton; + + static void _bind_methods(); + + // Implemented by addons as end points for the proxy functions + bool _initialize(String p_project_root_path); + bool _get_is_vcs_intialized(); + Dictionary _get_modified_files_data(); + void _stage_file(String p_file_path); + void _unstage_file(String p_file_path); + void _commit(String p_msg); + Array _get_file_diff(String p_file_path); + bool _shut_down(); + String _get_project_name(); + String _get_vcs_name(); + +public: + static EditorVCSInterface *get_singleton(); + static void set_singleton(EditorVCSInterface *p_singleton); + + bool is_addon_ready(); + + // Proxy functions to the editor for use + bool initialize(String p_project_root_path); + bool get_is_vcs_intialized(); + Dictionary get_modified_files_data(); + void stage_file(String p_file_path); + void unstage_file(String p_file_path); + void commit(String p_msg); + Array get_file_diff(String p_file_path); + bool shut_down(); + String get_project_name(); + String get_vcs_name(); + + EditorVCSInterface(); + virtual ~EditorVCSInterface(); +}; + +#endif // !EDITOR_VCS_INTERFACE_H diff --git a/editor/icons/icon_auto_key.svg b/editor/icons/icon_auto_key.svg index cbafe1ac38..3d5569397f 100644 --- a/editor/icons/icon_auto_key.svg +++ b/editor/icons/icon_auto_key.svg @@ -1,56 +1 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16" - height="16" - version="1.1" - viewBox="0 0 16 16" - id="svg6" - sodipodi:docname="icon_auto_key.svg" - inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> - <metadata - id="metadata12"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs10" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1854" - inkscape:window-height="1016" - id="namedview8" - showgrid="false" - inkscape:zoom="10.429825" - inkscape:cx="10.199345" - inkscape:cy="-4.0344119" - inkscape:window-x="66" - inkscape:window-y="27" - inkscape:window-maximized="1" - inkscape:current-layer="svg6" /> - <path - style="fill:#e0e0e0;fill-opacity:1;stroke-width:0.0333107" - d="M 3.5469681,13.426786 C 2.7965829,13.263778 2.2774312,12.503915 2.4037297,11.753472 c 0.1081234,-0.642451 0.6006808,-1.135008 1.2431317,-1.243131 0.9667125,-0.162696 1.8555225,0.726112 1.6928259,1.692826 -0.103766,0.616558 -0.5592173,1.098057 -1.1588427,1.225117 -0.2719576,0.05763 -0.3626872,0.05741 -0.6338765,-0.0014 z m 8.0861339,-0.08275 c -0.746862,-0.13829 -1.23937,-0.720718 -1.23937,-1.465649 0,-0.527377 0.244831,-0.978806 0.679757,-1.253362 0.471386,-0.297574 1.114188,-0.297574 1.585574,0 0.682727,0.430986 0.892336,1.362194 0.460575,2.046149 -0.307786,0.487563 -0.940521,0.773963 -1.486536,0.672862 z M 0.60726032,9.8305658 V 7.7161233 L 1.1770842,7.7070075 1.7469079,7.6978939 3.1889882,5.1995916 4.6310686,2.7012893 h 3.1726318 3.1726316 l 1.442755,2.4983023 1.442755,2.4983023 0.651097,0.00903 0.651096,0.00903 v 2.1145264 2.1145257 h -0.566282 -0.566281 v -0.161225 c 0,-0.234927 -0.113135,-0.639704 -0.255664,-0.914727 -0.16895,-0.326004 -0.574198,-0.731251 -0.900202,-0.9002019 -0.656732,-0.3403483 -1.428549,-0.3403483 -2.085281,0 -0.326004,0.1689519 -0.731252,0.5741989 -0.9002019,0.9002029 -0.1425297,0.275023 -0.2556639,0.6798 -0.2556639,0.914727 v 0.161225 H 7.8570969 6.0797346 L 6.0617736,11.686851 C 6.006289,10.889347 5.447548,10.170679 4.6603773,9.884336 4.4466221,9.8065798 4.3737631,9.797427 3.9716406,9.7978134 3.5871254,9.7981885 3.4905638,9.809405 3.3054265,9.8752358 2.5067319,10.159236 1.9362359,10.884501 1.8813215,11.68568 l -0.017772,0.259329 H 1.2354063 0.60726287 Z M 12.399247,7.7466889 c 0,-0.037287 -0.02623,-0.1073444 -0.0583,-0.1556843 -0.03206,-0.04834 -0.561225,-0.958444 -1.17592,-2.0224529 L 10.047407,3.6339894 7.6977565,3.6254406 C 5.4917229,3.6174174 5.3450379,3.6204563 5.2979001,3.6754094 5.1898818,3.8013046 2.9723198,7.6840061 2.9723198,7.7472381 c 0,0.067139 0.00758,0.067247 4.7134636,0.067247 h 4.7134636 z" - id="path6243" - inkscape:connector-curvature="0" /> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m5 3-3 5h-1v4h1.0507812a2.5 2.5 0 0 1 2.4492188-2 2.5 2.5 0 0 1 2.4453125 2h2.1054687a2.5 2.5 0 0 1 2.4492188-2 2.5 2.5 0 0 1 2.445312 2h1.054688v-4h-1l-4-5zm1 1h3l3 4h-8z" stroke-width=".033311"/><circle cx="4.5" cy="12.5" r="1.5"/><circle cx="11.5" cy="12.5" r="1.5"/></g></svg>
\ No newline at end of file diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index f42716c827..173079b6de 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -477,6 +477,19 @@ void AnimationPlayerEditor::_select_anim_by_name(const String &p_anim) { _animation_selected(idx); } +double AnimationPlayerEditor::_get_editor_step() const { + + // Returns the effective snapping value depending on snapping modifiers, or 0 if snapping is disabled. + if (track_editor->is_snap_enabled()) { + const String current = player->get_assigned_animation(); + const Ref<Animation> anim = player->get_animation(current); + // Use more precise snapping when holding Shift + return Input::get_singleton()->is_key_pressed(KEY_SHIFT) ? anim->get_step() * 0.25 : anim->get_step(); + } + + return 0.0; +} + void AnimationPlayerEditor::_animation_name_edited() { player->stop(); @@ -1017,7 +1030,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) { float pos = CLAMP(anim->get_length() * (p_value / frame->get_max()), 0, anim->get_length()); if (track_editor->is_snap_enabled()) { - pos = Math::stepify(pos, anim->get_step()); + pos = Math::stepify(pos, _get_editor_step()); } if (player->is_valid() && !p_set) { @@ -1068,7 +1081,7 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) Ref<Animation> anim = player->get_animation(player->get_assigned_animation()); updating = true; - frame->set_value(Math::stepify(p_pos, track_editor->is_snap_enabled() ? anim->get_step() : 0)); + frame->set_value(Math::stepify(p_pos, _get_editor_step())); updating = false; _seek_value_changed(p_pos, !p_drag); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 2ab2df68e6..4ad30675ec 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -161,6 +161,7 @@ class AnimationPlayerEditor : public VBoxContainer { } onion; void _select_anim_by_name(const String &p_anim); + double _get_editor_step() const; void _play_pressed(); void _play_from_pressed(); void _play_bw_pressed(); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 894e5c7298..60b5f017d2 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -586,7 +586,7 @@ void EditorAssetLibrary::_notification(int p_what) { } break; case NOTIFICATION_VISIBILITY_CHANGED: { - if (is_visible()) { + if (is_visible() && initial_loading) { _repository_changed(0); // Update when shown for the first time. } } break; @@ -1133,6 +1133,8 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const } break; case REQUESTING_SEARCH: { + initial_loading = false; + // The loading text only needs to be displayed before the first page is loaded library_loading->hide(); @@ -1328,6 +1330,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { requesting = REQUESTING_NONE; templates_only = p_templates_only; + initial_loading = true; VBoxContainer *library_main = memnew(VBoxContainer); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index b17a6dfe54..6a3158889e 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -206,6 +206,7 @@ class EditorAssetLibrary : public PanelContainer { HTTPRequest *request; bool templates_only; + bool initial_loading; enum Support { SUPPORT_OFFICIAL, diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 5d3cef4c34..c2b6031e60 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -167,10 +167,20 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) { _has_undo_data = true; } + const float curve_amplitude = curve.get_max_value() - curve.get_min_value(); + // Snap to "round" coordinates when holding Ctrl. + // Be more precise when holding Shift as well. + float snap_threshold; + if (mm.get_control()) { + snap_threshold = mm.get_shift() ? 0.025 : 0.1; + } else { + snap_threshold = 0.0; + } + if (_selected_tangent == TANGENT_NONE) { // Drag point - Vector2 point_pos = get_world_pos(mpos); + Vector2 point_pos = get_world_pos(mpos).snapped(Vector2(snap_threshold, snap_threshold * curve_amplitude)); int i = curve.set_point_offset(_selected_point, point_pos.x); // The index may change if the point is dragged across another one @@ -188,8 +198,8 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) { } else { // Drag tangent - Vector2 point_pos = curve.get_point_position(_selected_point); - Vector2 control_pos = get_world_pos(mpos); + const Vector2 point_pos = curve.get_point_position(_selected_point); + const Vector2 control_pos = get_world_pos(mpos).snapped(Vector2(snap_threshold, snap_threshold * curve_amplitude)); Vector2 dir = (control_pos - point_pos).normalized(); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index edc454ad1c..3b300a7ad5 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1741,6 +1741,13 @@ ScriptTextEditor::ScriptTextEditor() { color_panel->add_child(color_picker); color_picker->connect("color_changed", this, "_color_changed"); + // get default color picker mode from editor settings + int default_color_mode = EDITOR_GET("interface/inspector/default_color_picker_mode"); + if (default_color_mode == 1) + color_picker->set_hsv_mode(true); + else if (default_color_mode == 2) + color_picker->set_raw_mode(true); + edit_hb = memnew(HBoxContainer); edit_menu = memnew(MenuButton); diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp new file mode 100644 index 0000000000..d2873fb8f1 --- /dev/null +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -0,0 +1,565 @@ +#include "version_control_editor_plugin.h" +#include "core/script_language.h" +#include "editor/editor_file_system.h" +#include "editor/editor_node.h" + +VersionControlEditorPlugin *VersionControlEditorPlugin::singleton = NULL; + +void VersionControlEditorPlugin::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_selected_a_vcs"), &VersionControlEditorPlugin::_selected_a_vcs); + ClassDB::bind_method(D_METHOD("_initialize_vcs"), &VersionControlEditorPlugin::_initialize_vcs); + ClassDB::bind_method(D_METHOD("_send_commit_msg"), &VersionControlEditorPlugin::_send_commit_msg); + ClassDB::bind_method(D_METHOD("_refresh_stage_area"), &VersionControlEditorPlugin::_refresh_stage_area); + ClassDB::bind_method(D_METHOD("_stage_all"), &VersionControlEditorPlugin::_stage_all); + ClassDB::bind_method(D_METHOD("_stage_selected"), &VersionControlEditorPlugin::_stage_selected); + ClassDB::bind_method(D_METHOD("_view_file_diff"), &VersionControlEditorPlugin::_view_file_diff); + ClassDB::bind_method(D_METHOD("_refresh_file_diff"), &VersionControlEditorPlugin::_refresh_file_diff); + ClassDB::bind_method(D_METHOD("popup_vcs_set_up_dialog"), &VersionControlEditorPlugin::popup_vcs_set_up_dialog); + + // Used to track the status of files in the staging area + BIND_ENUM_CONSTANT(CHANGE_TYPE_NEW); + BIND_ENUM_CONSTANT(CHANGE_TYPE_MODIFIED); + BIND_ENUM_CONSTANT(CHANGE_TYPE_RENAMED); + BIND_ENUM_CONSTANT(CHANGE_TYPE_DELETED); + BIND_ENUM_CONSTANT(CHANGE_TYPE_TYPECHANGE); +} + +void VersionControlEditorPlugin::_selected_a_vcs(int p_id) { + + List<StringName> available_addons = get_available_vcs_names(); + const StringName selected_vcs = set_up_choice->get_item_text(p_id); + + if (available_addons.find(selected_vcs) != NULL) { + + set_up_init_button->set_disabled(false); + } else { + + set_up_init_button->set_disabled(true); + } +} + +void VersionControlEditorPlugin::_populate_available_vcs_names() { + + static bool called = false; + + if (!called) { + + set_up_choice->add_item("Select an available VCS"); + + fetch_available_vcs_addon_names(); + List<StringName> available_addons = get_available_vcs_names(); + for (int i = 0; i < available_addons.size(); i++) { + + set_up_choice->add_item(available_addons[i]); + } + + called = true; + } +} + +VersionControlEditorPlugin *VersionControlEditorPlugin::get_singleton() { + + return singleton ? singleton : memnew(VersionControlEditorPlugin); +} + +void VersionControlEditorPlugin::popup_vcs_set_up_dialog(const Control *p_gui_base) { + + Size2 popup_size = Size2(400, 100); + Size2 window_size = p_gui_base->get_viewport_rect().size; + popup_size.x = MIN(window_size.x * 0.5, popup_size.x); + popup_size.y = MIN(window_size.y * 0.5, popup_size.y); + + if (get_is_vcs_intialized()) { + + set_up_init_button->set_disabled(true); + } + + _populate_available_vcs_names(); + + set_up_dialog->popup_centered_clamped(popup_size * EDSCALE); +} + +void VersionControlEditorPlugin::_initialize_vcs() { + + register_editor(); + + if (EditorVCSInterface::get_singleton()) { + + ERR_EXPLAIN(EditorVCSInterface::get_singleton()->get_vcs_name() + " is already active"); + return; + } + + int id = set_up_choice->get_selected_id(); + String selected_addon = set_up_choice->get_item_text(id); + + String path = ScriptServer::get_global_class_path(selected_addon); + Ref<Script> script = ResourceLoader::load(path); + if (!script.is_valid()) { + + ERR_EXPLAIN("VCS Addon path is invalid"); + } + + EditorVCSInterface *vcs_interface = memnew(EditorVCSInterface); + ScriptInstance *addon_script_instance = script->instance_create(vcs_interface); + if (!addon_script_instance) { + + ERR_FAIL_NULL(addon_script_instance); + return; + } + + // The addon is attached as a script to the VCS interface as a proxy end-point + vcs_interface->set_script_and_instance(script.get_ref_ptr(), addon_script_instance); + + EditorVCSInterface::set_singleton(vcs_interface); + EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_refresh_stage_area"); + + String res_dir = OS::get_singleton()->get_resource_dir(); + if (!EditorVCSInterface::get_singleton()->initialize(res_dir)) { + + ERR_EXPLAIN("VCS was not initialized"); + } + + _refresh_stage_area(); +} + +void VersionControlEditorPlugin::_send_commit_msg() { + + String msg = commit_message->get_text(); + if (msg == "") { + + commit_status->set_text(TTR("No commit message was provided")); + return; + } + + if (EditorVCSInterface::get_singleton()) { + + if (staged_files_count == 0) { + + commit_status->set_text(TTR("No files added to stage")); + return; + } + + EditorVCSInterface::get_singleton()->commit(msg); + + commit_message->set_text(""); + version_control_dock_button->set_pressed(false); + } else { + + WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu"); + } + + _update_commit_status(); + _refresh_stage_area(); + _clear_file_diff(); +} + +void VersionControlEditorPlugin::_refresh_stage_area() { + + if (EditorVCSInterface::get_singleton()) { + + staged_files_count = 0; + clear_stage_area(); + + Dictionary modified_file_paths = EditorVCSInterface::get_singleton()->get_modified_files_data(); + String file_path; + for (int i = 0; i < modified_file_paths.size(); i++) { + + file_path = modified_file_paths.get_key_at_index(i); + TreeItem *found = stage_files->search_item_text(file_path, 0, true); + if (!found) { + + ChangeType change_index = (ChangeType)(int)modified_file_paths.get_value_at_index(i); + String change_text = file_path + " (" + change_type_to_strings[change_index] + ")"; + Color &change_color = change_type_to_color[change_index]; + TreeItem *new_item = stage_files->create_item(stage_files->get_root()); + new_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + new_item->set_text(0, change_text); + new_item->set_metadata(0, file_path); + new_item->set_custom_color(0, change_color); + new_item->set_checked(0, true); + new_item->set_editable(0, true); + } else { + + if (found->get_metadata(0) == diff_file_name->get_text()) { + + _refresh_file_diff(); + } + } + commit_status->set_text("New changes detected"); + } + } else { + + WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu.") + } +} + +void VersionControlEditorPlugin::_stage_selected() { + + if (!EditorVCSInterface::get_singleton()) { + + WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu"); + return; + } + + staged_files_count = 0; + TreeItem *root = stage_files->get_root(); + if (root) { + + TreeItem *file_entry = root->get_children(); + while (file_entry) { + + if (file_entry->is_checked(0)) { + + EditorVCSInterface::get_singleton()->stage_file(file_entry->get_metadata(0)); + file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_color("success_color", "Editor")); + staged_files_count++; + } else { + + EditorVCSInterface::get_singleton()->unstage_file(file_entry->get_metadata(0)); + file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); + } + + file_entry = file_entry->get_next(); + } + } + + _update_stage_status(); +} + +void VersionControlEditorPlugin::_stage_all() { + + if (!EditorVCSInterface::get_singleton()) { + + WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu"); + return; + } + + staged_files_count = 0; + TreeItem *root = stage_files->get_root(); + if (root) { + + TreeItem *file_entry = root->get_children(); + while (file_entry) { + + EditorVCSInterface::get_singleton()->stage_file(file_entry->get_metadata(0)); + file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_color("success_color", "Editor")); + file_entry->set_checked(0, true); + staged_files_count++; + + file_entry = file_entry->get_next(); + } + } + + _update_stage_status(); +} + +void VersionControlEditorPlugin::_view_file_diff() { + + version_control_dock_button->set_pressed(true); + + String file_path = stage_files->get_selected()->get_metadata(0); + + _display_file_diff(file_path); +} + +void VersionControlEditorPlugin::_display_file_diff(String p_file_path) { + + Array diff_content = EditorVCSInterface::get_singleton()->get_file_diff(p_file_path); + + diff_file_name->set_text(p_file_path); + + diff->clear(); + diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_font("source", "EditorFonts")); + for (int i = 0; i < diff_content.size(); i++) { + + Dictionary line_result = diff_content[i]; + + if (line_result["status"] == "+") { + + diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_color("success_color", "Editor")); + } else if (line_result["status"] == "-") { + + diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); + } else { + + diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_color("font_color", "Label")); + } + + diff->add_text((String)line_result["content"]); + + diff->pop(); + } + diff->pop(); +} + +void VersionControlEditorPlugin::_refresh_file_diff() { + + String open_file = diff_file_name->get_text(); + if (open_file != "") { + + _display_file_diff(diff_file_name->get_text()); + } +} + +void VersionControlEditorPlugin::_clear_file_diff() { + + diff->clear(); + diff_file_name->set_text(""); + version_control_dock_button->set_pressed(false); +} + +void VersionControlEditorPlugin::_update_stage_status() { + + String status; + if (staged_files_count == 1) { + + status = "Stage contains 1 file"; + } else { + + status = "Stage contains " + String::num_int64(staged_files_count) + " files"; + } + commit_status->set_text(status); +} + +void VersionControlEditorPlugin::_update_commit_status() { + + String status; + if (staged_files_count == 1) { + + status = "Committed 1 file"; + } else { + + status = "Committed " + String::num_int64(staged_files_count) + " files "; + } + commit_status->set_text(status); + staged_files_count = 0; +} + +void VersionControlEditorPlugin::register_editor() { + + if (!EditorVCSInterface::get_singleton()) { + + EditorNode::get_singleton()->add_control_to_dock(EditorNode::DOCK_SLOT_RIGHT_UL, version_commit_dock); + TabContainer *dock_vbc = (TabContainer *)version_commit_dock->get_parent_control(); + dock_vbc->set_tab_title(version_commit_dock->get_index(), TTR("Commit")); + + ToolButton *vc = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Version Control"), version_control_dock); + set_version_control_tool_button(vc); + } +} + +void VersionControlEditorPlugin::fetch_available_vcs_addon_names() { + + ScriptServer::get_global_class_list(&available_addons); +} + +void VersionControlEditorPlugin::clear_stage_area() { + + stage_files->get_root()->clear_children(); +} + +void VersionControlEditorPlugin::shut_down() { + + if (EditorVCSInterface::get_singleton()) { + + EditorFileSystem::get_singleton()->disconnect("filesystem_changed", this, "_refresh_stage_area"); + EditorVCSInterface::get_singleton()->shut_down(); + memdelete(EditorVCSInterface::get_singleton()); + EditorVCSInterface::set_singleton(NULL); + + EditorNode::get_singleton()->remove_control_from_dock(version_commit_dock); + EditorNode::get_singleton()->remove_bottom_panel_item(version_control_dock); + } +} + +bool VersionControlEditorPlugin::get_is_vcs_intialized() const { + + return EditorVCSInterface::get_singleton() ? EditorVCSInterface::get_singleton()->get_is_vcs_intialized() : false; +} + +const String VersionControlEditorPlugin::get_vcs_name() const { + + return EditorVCSInterface::get_singleton() ? EditorVCSInterface::get_singleton()->get_vcs_name() : ""; +} + +VersionControlEditorPlugin::VersionControlEditorPlugin() { + + singleton = this; + staged_files_count = 0; + + version_control_actions = memnew(PopupMenu); + version_control_actions->set_v_size_flags(BoxContainer::SIZE_SHRINK_CENTER); + + set_up_dialog = memnew(AcceptDialog); + set_up_dialog->set_title(TTR("Set Up Version Control")); + set_up_dialog->set_custom_minimum_size(Size2(400, 100)); + version_control_actions->add_child(set_up_dialog); + + set_up_ok_button = set_up_dialog->get_ok(); + set_up_ok_button->set_disabled(false); + set_up_ok_button->set_text(TTR("Close")); + + set_up_vbc = memnew(VBoxContainer); + set_up_vbc->set_alignment(VBoxContainer::ALIGN_CENTER); + set_up_dialog->add_child(set_up_vbc); + + set_up_hbc = memnew(HBoxContainer); + set_up_hbc->set_h_size_flags(HBoxContainer::SIZE_EXPAND_FILL); + set_up_vbc->add_child(set_up_hbc); + + set_up_vcs_status = memnew(RichTextLabel); + set_up_vcs_status->set_text(TTR("VCS Addon is not initialized")); + set_up_vbc->add_child(set_up_vcs_status); + + set_up_vcs_label = memnew(Label); + set_up_vcs_label->set_text(TTR("Version Control System")); + set_up_hbc->add_child(set_up_vcs_label); + + set_up_choice = memnew(OptionButton); + set_up_choice->set_h_size_flags(HBoxContainer::SIZE_EXPAND_FILL); + set_up_choice->connect("item_selected", this, "_selected_a_vcs"); + set_up_hbc->add_child(set_up_choice); + + set_up_init_settings = NULL; + + set_up_init_button = memnew(Button); + set_up_init_button->set_disabled(true); + set_up_init_button->set_text(TTR("Initialize")); + set_up_init_button->connect("pressed", this, "_initialize_vcs"); + set_up_vbc->add_child(set_up_init_button); + + version_control_actions->set_v_size_flags(PopupMenu::SIZE_EXPAND_FILL); + version_control_actions->set_h_size_flags(PopupMenu::SIZE_EXPAND_FILL); + + version_commit_dock = memnew(VBoxContainer); + version_commit_dock->set_visible(false); + + commit_box_vbc = memnew(VBoxContainer); + commit_box_vbc->set_alignment(VBoxContainer::ALIGN_BEGIN); + commit_box_vbc->set_h_size_flags(VBoxContainer::SIZE_EXPAND_FILL); + commit_box_vbc->set_v_size_flags(VBoxContainer::SIZE_EXPAND_FILL); + version_commit_dock->add_child(commit_box_vbc); + + stage_tools = memnew(HSplitContainer); + stage_tools->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN_COLLAPSED); + commit_box_vbc->add_child(stage_tools); + + staging_area_label = memnew(Label); + staging_area_label->set_h_size_flags(Label::SIZE_EXPAND_FILL); + staging_area_label->set_text(TTR("Staging area")); + stage_tools->add_child(staging_area_label); + + refresh_button = memnew(Button); + refresh_button->set_tooltip(TTR("Detect new changes")); + refresh_button->set_text(TTR("Refresh")); + refresh_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Reload", "EditorIcons")); + refresh_button->connect("pressed", this, "_refresh_stage_area"); + stage_tools->add_child(refresh_button); + + stage_files = memnew(Tree); + stage_files->set_h_size_flags(Tree::SIZE_EXPAND_FILL); + stage_files->set_v_size_flags(Tree::SIZE_EXPAND_FILL); + stage_files->set_columns(1); + stage_files->set_column_title(0, TTR("Changes")); + stage_files->set_column_titles_visible(true); + stage_files->set_allow_reselect(true); + stage_files->set_allow_rmb_select(true); + stage_files->set_select_mode(Tree::SelectMode::SELECT_MULTI); + stage_files->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); + stage_files->connect("cell_selected", this, "_view_file_diff"); + stage_files->create_item(); + stage_files->set_hide_root(true); + commit_box_vbc->add_child(stage_files); + + change_type_to_strings[CHANGE_TYPE_NEW] = TTR("New"); + change_type_to_strings[CHANGE_TYPE_MODIFIED] = TTR("Modified"); + change_type_to_strings[CHANGE_TYPE_RENAMED] = TTR("Renamed"); + change_type_to_strings[CHANGE_TYPE_DELETED] = TTR("Deleted"); + change_type_to_strings[CHANGE_TYPE_TYPECHANGE] = TTR("Typechange"); + + change_type_to_color[CHANGE_TYPE_NEW] = EditorNode::get_singleton()->get_gui_base()->get_color("success_color", "Editor"); + change_type_to_color[CHANGE_TYPE_MODIFIED] = EditorNode::get_singleton()->get_gui_base()->get_color("warning_color", "Editor"); + change_type_to_color[CHANGE_TYPE_RENAMED] = EditorNode::get_singleton()->get_gui_base()->get_color("disabled_font_color", "Editor"); + change_type_to_color[CHANGE_TYPE_DELETED] = EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor"); + change_type_to_color[CHANGE_TYPE_TYPECHANGE] = EditorNode::get_singleton()->get_gui_base()->get_color("font_color", "Editor"); + + stage_buttons = memnew(HSplitContainer); + stage_buttons->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN_COLLAPSED); + commit_box_vbc->add_child(stage_buttons); + + stage_selected_button = memnew(Button); + stage_selected_button->set_h_size_flags(Button::SIZE_EXPAND_FILL); + stage_selected_button->set_text(TTR("Stage Selected")); + stage_selected_button->connect("pressed", this, "_stage_selected"); + stage_buttons->add_child(stage_selected_button); + + stage_all_button = memnew(Button); + stage_all_button->set_text(TTR("Stage All")); + stage_all_button->connect("pressed", this, "_stage_all"); + stage_buttons->add_child(stage_all_button); + + commit_box_vbc->add_child(memnew(HSeparator)); + + commit_message = memnew(TextEdit); + commit_message->set_h_size_flags(Control::SIZE_EXPAND_FILL); + commit_message->set_h_grow_direction(Control::GrowDirection::GROW_DIRECTION_BEGIN); + commit_message->set_v_grow_direction(Control::GrowDirection::GROW_DIRECTION_END); + commit_message->set_custom_minimum_size(Size2(200, 100)); + commit_message->set_wrap_enabled(true); + commit_message->set_text(TTR("Add a commit message")); + commit_box_vbc->add_child(commit_message); + + commit_button = memnew(Button); + commit_button->set_text(TTR("Commit Changes")); + commit_button->connect("pressed", this, "_send_commit_msg"); + commit_box_vbc->add_child(commit_button); + + commit_status = memnew(Label); + commit_status->set_align(Label::ALIGN_CENTER); + commit_box_vbc->add_child(commit_status); + + version_control_dock = memnew(PanelContainer); + version_control_dock->set_v_size_flags(Control::SIZE_EXPAND_FILL); + version_control_dock->hide(); + + diff_vbc = memnew(VBoxContainer); + diff_vbc->set_h_size_flags(HBoxContainer::SIZE_FILL); + diff_vbc->set_v_size_flags(HBoxContainer::SIZE_FILL); + version_control_dock->add_child(diff_vbc); + + diff_hbc = memnew(HBoxContainer); + diff_hbc->set_h_size_flags(HBoxContainer::SIZE_FILL); + diff_vbc->add_child(diff_hbc); + + diff_heading = memnew(Label); + diff_heading->set_text(TTR("Status")); + diff_heading->set_tooltip(TTR("View file diffs before commiting them to the latest version")); + diff_hbc->add_child(diff_heading); + + diff_file_name = memnew(Label); + diff_file_name->set_text(TTR("No file diff is active")); + diff_file_name->set_h_size_flags(Label::SIZE_EXPAND_FILL); + diff_file_name->set_align(Label::ALIGN_RIGHT); + diff_hbc->add_child(diff_file_name); + + diff_refresh_button = memnew(Button); + diff_refresh_button->set_tooltip(TTR("Detect changes in file diff")); + diff_refresh_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Reload", "EditorIcons")); + diff_refresh_button->connect("pressed", this, "_refresh_file_diff"); + diff_hbc->add_child(diff_refresh_button); + + diff = memnew(RichTextLabel); + diff->set_h_size_flags(TextEdit::SIZE_EXPAND_FILL); + diff->set_v_size_flags(TextEdit::SIZE_EXPAND_FILL); + diff->set_selection_enabled(true); + diff_vbc->add_child(diff); +} + +VersionControlEditorPlugin::~VersionControlEditorPlugin() { + + shut_down(); + memdelete(version_control_dock); + memdelete(version_commit_dock); + memdelete(version_control_actions); +} diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h new file mode 100644 index 0000000000..dd7660d587 --- /dev/null +++ b/editor/plugins/version_control_editor_plugin.h @@ -0,0 +1,116 @@ +#ifndef VERSION_CONTROL_EDITOR_PLUGIN_H +#define VERSION_CONTROL_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "editor/editor_vcs_interface.h" +#include "scene/gui/container.h" +#include "scene/gui/rich_text_label.h" +#include "scene/gui/text_edit.h" +#include "scene/gui/tree.h" + +class VersionControlEditorPlugin : public EditorPlugin { + + GDCLASS(VersionControlEditorPlugin, EditorPlugin) + +public: + enum ChangeType { + + CHANGE_TYPE_NEW = 0, + CHANGE_TYPE_MODIFIED = 1, + CHANGE_TYPE_RENAMED = 2, + CHANGE_TYPE_DELETED = 3, + CHANGE_TYPE_TYPECHANGE = 4 + }; + +private: + static VersionControlEditorPlugin *singleton; + + int staged_files_count; + List<StringName> available_addons; + + PopupMenu *version_control_actions; + AcceptDialog *set_up_dialog; + VBoxContainer *set_up_vbc; + HBoxContainer *set_up_hbc; + Label *set_up_vcs_label; + OptionButton *set_up_choice; + PanelContainer *set_up_init_settings; + Button *set_up_init_button; + RichTextLabel *set_up_vcs_status; + Button *set_up_ok_button; + + HashMap<ChangeType, String> change_type_to_strings; + HashMap<ChangeType, Color> change_type_to_color; + + VBoxContainer *version_commit_dock; + VBoxContainer *commit_box_vbc; + HSplitContainer *stage_tools; + Tree *stage_files; + TreeItem *new_files; + TreeItem *modified_files; + TreeItem *renamed_files; + TreeItem *deleted_files; + TreeItem *typechange_files; + Label *staging_area_label; + HSplitContainer *stage_buttons; + Button *stage_all_button; + Button *stage_selected_button; + Button *refresh_button; + TextEdit *commit_message; + Button *commit_button; + Label *commit_status; + + PanelContainer *version_control_dock; + ToolButton *version_control_dock_button; + VBoxContainer *diff_vbc; + HBoxContainer *diff_hbc; + Button *diff_refresh_button; + Label *diff_file_name; + Label *diff_heading; + RichTextLabel *diff; + + void _populate_available_vcs_names(); + void _selected_a_vcs(int p_id); + void _initialize_vcs(); + void _send_commit_msg(); + void _refresh_stage_area(); + void _stage_selected(); + void _stage_all(); + void _view_file_diff(); + void _display_file_diff(String p_file_path); + void _refresh_file_diff(); + void _clear_file_diff(); + void _update_stage_status(); + void _update_commit_status(); + + friend class EditorVCSInterface; + +protected: + static void _bind_methods(); + +public: + static VersionControlEditorPlugin *get_singleton(); + + void popup_vcs_set_up_dialog(const Control *p_gui_base); + void set_version_control_tool_button(ToolButton *p_button) { version_control_dock_button = p_button; } + + PopupMenu *get_version_control_actions_panel() const { return version_control_actions; } + VBoxContainer *get_version_commit_dock() const { return version_commit_dock; } + PanelContainer *get_version_control_dock() const { return version_control_dock; } + + List<StringName> get_available_vcs_names() const { return available_addons; } + bool get_is_vcs_intialized() const; + const String get_vcs_name() const; + + void register_editor(); + void fetch_available_vcs_addon_names(); + void clear_stage_area(); + void shut_down(); + + VersionControlEditorPlugin(); + ~VersionControlEditorPlugin(); +}; + +VARIANT_ENUM_CAST(VersionControlEditorPlugin::ChangeType); + +#endif // !VERSION_CONTROL_EDITOR_PLUGIN_H diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 899343c0f8..ecb272876d 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -859,6 +859,13 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: add_child(color_picker); color_picker->hide(); color_picker->connect("color_changed", this, "_color_changed"); + + // get default color picker mode from editor settings + int default_color_mode = EDITOR_GET("interface/inspector/default_color_picker_mode"); + if (default_color_mode == 1) + color_picker->set_hsv_mode(true); + else if (default_color_mode == 2) + color_picker->set_raw_mode(true); } color_picker->show(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index f0114b393d..16f1575757 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -759,7 +759,16 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { _delete_confirm(); } else { - delete_dialog->set_text(TTR("Delete Node(s)?")); + if (remove_list.size() > 1) { + delete_dialog->set_text(vformat(TTR("Delete %d nodes?"), remove_list.size())); + } else { + delete_dialog->set_text(vformat(TTR("Delete node \"%s\"?"), remove_list[0]->get_name())); + } + + // Resize the dialog to its minimum size. + // This prevents the dialog from being too wide after displaying + // a deletion confirmation for a node with a long name. + delete_dialog->set_size(Size2()); delete_dialog->popup_centered_minsize(); } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 43f540e688..f1fc4eb950 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -299,16 +299,34 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { if (p_node == get_scene_node() && p_node->get_scene_inherited_state().is_valid()) { item->add_button(0, get_icon("InstanceOptions", "EditorIcons"), BUTTON_SUBSCENE, false, TTR("Open in Editor")); - item->set_tooltip(0, TTR("Inherits:") + " " + p_node->get_scene_inherited_state()->get_path() + "\n" + TTR("Type:") + " " + p_node->get_class()); - } else if (p_node != get_scene_node() && p_node->get_filename() != "" && can_open_instance) { + String tooltip = TTR("Inherits:") + " " + p_node->get_scene_inherited_state()->get_path() + "\n" + TTR("Type:") + " " + p_node->get_class(); + if (p_node->get_editor_description() != String()) { + tooltip += "\n\n" + p_node->get_editor_description(); + } + + item->set_tooltip(0, tooltip); + } else if (p_node != get_scene_node() && p_node->get_filename() != "" && can_open_instance) { item->add_button(0, get_icon("InstanceOptions", "EditorIcons"), BUTTON_SUBSCENE, false, TTR("Open in Editor")); - item->set_tooltip(0, TTR("Instance:") + " " + p_node->get_filename() + "\n" + TTR("Type:") + " " + p_node->get_class()); + + String tooltip = TTR("Instance:") + " " + p_node->get_filename() + "\n" + TTR("Type:") + " " + p_node->get_class(); + if (p_node->get_editor_description() != String()) { + tooltip += "\n\n" + p_node->get_editor_description(); + } + + item->set_tooltip(0, tooltip); } else { StringName type = EditorNode::get_singleton()->get_object_custom_type_name(p_node); - if (type == StringName()) + if (type == StringName()) { type = p_node->get_class(); - item->set_tooltip(0, String(p_node->get_name()) + "\n" + TTR("Type:") + " " + type); + } + + String tooltip = TTR("Type:") + " " + type; + if (p_node->get_editor_description() != String()) { + tooltip += "\n\n" + p_node->get_editor_description(); + } + + item->set_tooltip(0, tooltip); } if (can_open_instance && undo_redo) { //Show buttons only when necessary(SceneTreeDock) to avoid crashes diff --git a/misc/dist/html/full-size.html b/misc/dist/html/full-size.html index 0e8a41a9fc..349420b3f3 100644 --- a/misc/dist/html/full-size.html +++ b/misc/dist/html/full-size.html @@ -180,8 +180,8 @@ $GODOT_HEAD_INCLUDE [statusProgress, statusIndeterminate, statusNotice].forEach(elem => { elem.style.display = 'none'; }); - if (animateStatusIndeterminate in animationCallbacks) { - animationCallbacks.erase(animateStatusIndeterminate); + animationCallbacks = animationCallbacks.filter(function(value) { + return (value != animateStatusIndeterminate); } switch (mode) { case 'progress': diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h index 8d82cd3e39..56d89ffea7 100644 --- a/modules/assimp/import_state.h +++ b/modules/assimp/import_state.h @@ -79,7 +79,7 @@ struct ImportState { struct AssimpImageData { Ref<Image> raw_image; Ref<ImageTexture> texture; - aiTextureMapMode *map_mode = NULL; + aiTextureMapMode map_mode[2]; }; /** Recursive state is used to push state into functions instead of specifying them diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 925dbda620..b4c38e4d40 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1936,9 +1936,18 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context Ref<GDScript> script = base_type.script_type; if (script.is_valid()) { if (!_static && !p_only_functions) { - for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { - ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); - r_result.insert(option.display, option); + if (p_context.base && p_context.base->get_script_instance()) { + List<PropertyInfo> members; + p_context.base->get_script_instance()->get_property_list(&members); + for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); + } + } else { + for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); + } } } if (!p_only_functions) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 9c5b78c8f3..e96bf0238a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -6585,7 +6585,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { return DataType(); } } - if (check_types && !node_type.has_type) { + if (check_types && !node_type.has_type && base_type.kind == DataType::BUILTIN) { // Can infer indexing type for some variant types DataType result; result.has_type = true; diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index 15adf0a13b..ce34cd6a99 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -158,6 +158,11 @@ namespace Godot public static bool IsEqualApprox(real_t a, real_t b) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. real_t tolerance = Epsilon * Abs(a); if (tolerance < Epsilon) { tolerance = Epsilon; diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index b96f01bc2e..6cffc7f01d 100644 --- a/modules/mono/glue/Managed/Files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -48,7 +48,12 @@ namespace Godot public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) { + return true; + } + // Then check for approximate equality. return Abs(a - b) < tolerance; } } -}
\ No newline at end of file +} diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 7cc937a64a..9b6020e0fd 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1446,6 +1446,14 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) { void KinematicBody2D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { last_valid_transform = get_global_transform(); + + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector2(); } if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index ebac968cb4..0756be5fc8 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1387,6 +1387,18 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { return slide_colliders[p_bounce]; } +void KinematicBody::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector3(); + } +} + void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 0e2e614717..0967cb9cd5 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -315,6 +315,7 @@ private: Ref<KinematicCollision> _get_slide_collision(int p_bounce); protected: + void _notification(int p_what); static void _bind_methods(); public: diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 2609924f33..0f7d4466c8 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -783,10 +783,12 @@ float Tween::get_speed_scale() const { } bool Tween::start() { + + ERR_FAIL_COND_V_MSG(!is_inside_tree(), false, "Tween was not added to the SceneTree!"); + // Are there any pending updates? if (pending_update != 0) { // Start the tweens after deferring - call_deferred("start"); return true; } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 4ce3f18505..6b3e89af6c 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -39,162 +39,191 @@ Size2 Button::get_minimum_size() const { if (clip_text) minsize.width = 0; - Ref<Texture> _icon; - if (icon.is_null() && has_icon("icon")) - _icon = Control::get_icon("icon"); - else - _icon = icon; - - if (!_icon.is_null()) { - - minsize.height = MAX(minsize.height, _icon->get_height()); - minsize.width += _icon->get_width(); - if (xl_text != "") - minsize.width += get_constant("hseparation"); + if (!expand_icon) { + Ref<Texture> _icon; + if (icon.is_null() && has_icon("icon")) + _icon = Control::get_icon("icon"); + else + _icon = icon; + + if (!_icon.is_null()) { + + minsize.height = MAX(minsize.height, _icon->get_height()); + minsize.width += _icon->get_width(); + if (xl_text != "") + minsize.width += get_constant("hseparation"); + } } return get_stylebox("normal")->get_minimum_size() + minsize; } void Button::_set_internal_margin(Margin p_margin, float p_value) { + _internal_margin[p_margin] = p_value; } void Button::_notification(int p_what) { - if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { + switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: { - xl_text = tr(text); - minimum_size_changed(); - update(); - } + xl_text = tr(text); + minimum_size_changed(); + update(); + } break; + case NOTIFICATION_DRAW: { - if (p_what == NOTIFICATION_DRAW) { + RID ci = get_canvas_item(); + Size2 size = get_size(); + Color color; + Color color_icon(1, 1, 1, 1); - RID ci = get_canvas_item(); - Size2 size = get_size(); - Color color; - Color color_icon(1, 1, 1, 1); + Ref<StyleBox> style = get_stylebox("normal"); - Ref<StyleBox> style = get_stylebox("normal"); + switch (get_draw_mode()) { + case DRAW_NORMAL: { - switch (get_draw_mode()) { - - case DRAW_NORMAL: { + style = get_stylebox("normal"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color"); + if (has_color("icon_color_normal")) + color_icon = get_color("icon_color_normal"); + } break; + case DRAW_HOVER_PRESSED: { + + if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { + style = get_stylebox("hover_pressed"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + if (has_color("font_color_hover_pressed")) + color = get_color("font_color_hover_pressed"); + else + color = get_color("font_color"); + if (has_color("icon_color_hover_pressed")) + color_icon = get_color("icon_color_hover_pressed"); + + break; + } + FALLTHROUGH; + } + case DRAW_PRESSED: { - style = get_stylebox("normal"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color"); - if (has_color("icon_color_normal")) - color_icon = get_color("icon_color_normal"); - } break; - case DRAW_HOVER_PRESSED: { - if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { - style = get_stylebox("hover_pressed"); + style = get_stylebox("pressed"); if (!flat) style->draw(ci, Rect2(Point2(0, 0), size)); - if (has_color("font_color_hover_pressed")) - color = get_color("font_color_hover_pressed"); + if (has_color("font_color_pressed")) + color = get_color("font_color_pressed"); else color = get_color("font_color"); - if (has_color("icon_color_hover_pressed")) - color_icon = get_color("icon_color_hover_pressed"); + if (has_color("icon_color_pressed")) + color_icon = get_color("icon_color_pressed"); - break; - } - FALLTHROUGH; + } break; + case DRAW_HOVER: { + + style = get_stylebox("hover"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color_hover"); + if (has_color("icon_color_hover")) + color_icon = get_color("icon_color_hover"); + + } break; + case DRAW_DISABLED: { + + style = get_stylebox("disabled"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color_disabled"); + if (has_color("icon_color_disabled")) + color_icon = get_color("icon_color_disabled"); + + } break; } - case DRAW_PRESSED: { - - style = get_stylebox("pressed"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - if (has_color("font_color_pressed")) - color = get_color("font_color_pressed"); - else - color = get_color("font_color"); - if (has_color("icon_color_pressed")) - color_icon = get_color("icon_color_pressed"); - - } break; - case DRAW_HOVER: { - - style = get_stylebox("hover"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color_hover"); - if (has_color("icon_color_hover")) - color_icon = get_color("icon_color_hover"); - - } break; - case DRAW_DISABLED: { - - style = get_stylebox("disabled"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color_disabled"); - if (has_color("icon_color_disabled")) - color_icon = get_color("icon_color_disabled"); - - } break; - } - if (has_focus()) { + if (has_focus()) { - Ref<StyleBox> style2 = get_stylebox("focus"); - style2->draw(ci, Rect2(Point2(), size)); - } + Ref<StyleBox> style2 = get_stylebox("focus"); + style2->draw(ci, Rect2(Point2(), size)); + } - Ref<Font> font = get_font("font"); - Ref<Texture> _icon; - if (icon.is_null() && has_icon("icon")) - _icon = Control::get_icon("icon"); - else - _icon = icon; + Ref<Font> font = get_font("font"); + Ref<Texture> _icon; + if (icon.is_null() && has_icon("icon")) + _icon = Control::get_icon("icon"); + else + _icon = icon; - Point2 icon_ofs = (!_icon.is_null()) ? Point2(_icon->get_width() + get_constant("hseparation"), 0) : Point2(); - int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; - Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; + Rect2 icon_region = Rect2(); + if (!_icon.is_null()) { - switch (align) { - case ALIGN_LEFT: { + int valign = size.height - style->get_minimum_size().y; + if (is_disabled()) { + color_icon.a = 0.4; + } + + float icon_ofs_region = 0; if (_internal_margin[MARGIN_LEFT] > 0) { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); - } else { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + icon_ofs_region = _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); } - text_ofs.y += style->get_offset().y; - } break; - case ALIGN_CENTER: { - if (text_ofs.x < 0) - text_ofs.x = 0; - text_ofs += icon_ofs; - text_ofs += style->get_offset(); - } break; - case ALIGN_RIGHT: { - if (_internal_margin[MARGIN_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation"); + + if (expand_icon) { + Size2 _size = get_size() - style->get_offset() * 2; + _size.width -= get_constant("hseparation") + icon_ofs_region; + if (!clip_text) + _size.width -= get_font("font")->get_string_size(xl_text).width; + float icon_width = icon->get_width() * _size.height / icon->get_height(); + float icon_height = _size.height; + + if (icon_width > _size.width) { + icon_width = _size.width; + icon_height = icon->get_height() * icon_width / icon->get_width(); + } + + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); } else { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), icon->get_size()); } - text_ofs.y += style->get_offset().y; - } break; - } + } - text_ofs.y += font->get_ascent(); - font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); - if (!_icon.is_null()) { + Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_constant("hseparation"), 0) : Point2(); + int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; + Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; + + switch (align) { + case ALIGN_LEFT: { + if (_internal_margin[MARGIN_LEFT] > 0) { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); + } else { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + } + text_ofs.y += style->get_offset().y; + } break; + case ALIGN_CENTER: { + if (text_ofs.x < 0) + text_ofs.x = 0; + text_ofs += icon_ofs; + text_ofs += style->get_offset(); + } break; + case ALIGN_RIGHT: { + if (_internal_margin[MARGIN_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation"); + } else { + text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + } + text_ofs.y += style->get_offset().y; + } break; + } + + text_ofs.y += font->get_ascent(); + font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); - int valign = size.height - style->get_minimum_size().y; - if (is_disabled()) - color_icon.a = 0.4; - if (_internal_margin[MARGIN_LEFT] > 0) { - _icon->draw(ci, style->get_offset() + Point2(_internal_margin[MARGIN_LEFT] + get_constant("hseparation"), Math::floor((valign - _icon->get_height()) / 2.0)), color_icon); - } else { - _icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon); + if (!_icon.is_null() && icon_region.size.width > 0) { + draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), icon->get_size()), color_icon); } - } + } break; } } @@ -228,6 +257,18 @@ Ref<Texture> Button::get_icon() const { return icon; } +void Button::set_expand_icon(bool p_expand_icon) { + + expand_icon = p_expand_icon; + update(); + minimum_size_changed(); +} + +bool Button::is_expand_icon() const { + + return expand_icon; +} + void Button::set_flat(bool p_flat) { flat = p_flat; @@ -269,6 +310,8 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); + ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); + ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text); @@ -285,12 +328,14 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); } Button::Button(const String &p_text) { flat = false; clip_text = false; + expand_icon = false; set_mouse_filter(MOUSE_FILTER_STOP); set_text(p_text); align = ALIGN_CENTER; diff --git a/scene/gui/button.h b/scene/gui/button.h index 370809060e..1fff2cfda7 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -49,6 +49,7 @@ private: String text; String xl_text; Ref<Texture> icon; + bool expand_icon; bool clip_text; TextAlign align; float _internal_margin[4]; @@ -59,8 +60,6 @@ protected: static void _bind_methods(); public: - // - virtual Size2 get_minimum_size() const; void set_text(const String &p_text); @@ -69,6 +68,9 @@ public: void set_icon(const Ref<Texture> &p_icon); Ref<Texture> get_icon() const; + void set_expand_icon(bool p_expand_icon); + bool is_expand_icon() const; + void set_flat(bool p_flat); bool is_flat() const; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 1d529f4e72..6dd9e401f6 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -964,6 +964,7 @@ void ColorPickerButton::_update_picker() { popup->connect("popup_hide", this, "set_pressed", varray(false)); picker->set_pick_color(color); picker->set_edit_alpha(edit_alpha); + emit_signal("picker_created"); } } @@ -980,6 +981,7 @@ void ColorPickerButton::_bind_methods() { ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("popup_closed")); + ADD_SIGNAL(MethodInfo("picker_created")); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 034e9266f6..174c2fce7d 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2149,9 +2149,7 @@ bool Control::has_focus() const { void Control::grab_focus() { - if (!is_inside_tree()) { - ERR_FAIL_COND(!is_inside_tree()); - } + ERR_FAIL_COND(!is_inside_tree()); if (data.focus_mode == FOCUS_NONE) { WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus."); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 75f5f79873..09ef6f26bf 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -241,9 +241,13 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { float newofs = CLAMP(x / float(total_w), 0, 1); - //Snap to nearest point if holding shift - if (mm->get_shift()) { - float snap_threshold = 0.03; + // Snap to "round" coordinates if holding Ctrl. + // Be more precise if holding Shift as well + if (mm->get_control()) { + newofs = Math::stepify(newofs, mm->get_shift() ? 0.025 : 0.1); + } else if (mm->get_shift()) { + // Snap to nearest point if holding just Shift + const float snap_threshold = 0.03; float smallest_ofs = snap_threshold; bool found = false; int nearest_point = 0; diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index b5f949aeb7..e9b0bd8f38 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -205,24 +205,26 @@ void TextureButton::_notification(int p_what) { case STRETCH_KEEP_ASPECT_COVERED: { size = get_size(); Size2 tex_size = texdraw->get_size(); - Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height); - float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height; - Size2 scaledTexSize = tex_size * scale; - Point2 ofs2 = ((scaledTexSize - size) / scale).abs() / 2.0f; + Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height); + float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height; + Size2 scaled_tex_size = tex_size * scale; + Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f; _texture_region = Rect2(ofs2, size / scale); } break; } } + _position_rect = Rect2(ofs, size); - if (_tile) + if (_tile) { draw_texture_rect(texdraw, _position_rect, _tile); - else + } else { draw_texture_rect_region(texdraw, _position_rect, _texture_region); + } } else { _position_rect = Rect2(); } - if (has_focus() && focused.is_valid()) { + if (has_focus() && focused.is_valid()) { draw_texture_rect(focused, _position_rect, false); }; } break; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index b7451faad3..2a18436a5e 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1184,23 +1184,22 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (p_item->cells[i].selected && select_mode != SELECT_ROW) { - + if (select_mode != SELECT_ROW && (p_item->cells[i].selected || selected_item == p_item)) { Rect2i r(cell_rect.position, cell_rect.size); + if (p_item->cells[i].text.size() > 0) { float icon_width = p_item->cells[i].get_icon_size().width; r.position.x += icon_width; r.size.x -= icon_width; } p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); - if (has_focus()) { - cache.selected_focus->draw(ci, r); - } else { - cache.selected->draw(ci, r); - } - if (text_editor->is_visible_in_tree()) { - Vector2 ofs2(0, (text_editor->get_size().height - r.size.height) / 2); - text_editor->set_position(get_global_position() + r.position - ofs2); + + if (p_item->cells[i].selected) { + if (has_focus()) { + cache.selected_focus->draw(ci, r); + } else { + cache.selected->draw(ci, r); + } } } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0b3a193d18..bd01ca2886 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1875,6 +1875,19 @@ String Node::get_filename() const { return data.filename; } +void Node::set_editor_description(const String &p_editor_description) { + + set_meta("_editor_description_", p_editor_description); +} +String Node::get_editor_description() const { + + if (has_meta("_editor_description_")) { + return get_meta("_editor_description_"); + } else { + return ""; + } +} + void Node::set_editable_instance(Node *p_node, bool p_editable) { ERR_FAIL_NULL(p_node); @@ -2788,6 +2801,10 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config); ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config); + ClassDB::bind_method(D_METHOD("_set_editor_description", "editor_description"), &Node::set_editor_description); + ClassDB::bind_method(D_METHOD("_get_editor_description"), &Node::get_editor_description); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_editor_description", "_get_editor_description"); + ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); @@ -2860,10 +2877,6 @@ void Node::_bind_methods() { ADD_SIGNAL(MethodInfo("tree_exiting")); ADD_SIGNAL(MethodInfo("tree_exited")); - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/process" ),"set_process","is_processing") ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/physics_process" ), "set_physics_process","is_physics_processing") ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/input" ), "set_process_input","is_processing_input" ) ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); @@ -2887,9 +2900,6 @@ void Node::_bind_methods() { BIND_VMETHOD(MethodInfo("_unhandled_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_key_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEventKey"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_configuration_warning")); - - //ClassDB::bind_method(D_METHOD("get_child",&Node::get_child,PH("index"))); - //ClassDB::bind_method(D_METHOD("get_node",&Node::get_node,PH("path"))); } String Node::_get_name_num_separator() { diff --git a/scene/main/node.h b/scene/main/node.h index 67b40f6dfc..51a1436014 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -318,6 +318,9 @@ public: void set_filename(const String &p_filename); String get_filename() const; + void set_editor_description(const String &p_editor_description); + String get_editor_description() const; + void set_editable_instance(Node *p_node, bool p_editable); bool is_editable_instance(const Node *p_node) const; void set_editable_instances(const HashMap<NodePath, int> &p_editable_instances); @@ -363,8 +366,6 @@ public: Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const; #endif - //Node *clone_tree() const; - // used by editors, to save what has changed only void set_scene_instance_state(const Ref<SceneState> &p_state); Ref<SceneState> get_scene_instance_state() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 617a703855..6c89016ea3 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -33,6 +33,7 @@ #include "core/io/marshalls.h" #include "core/io/resource_loader.h" #include "core/message_queue.h" +#include "core/os/dir_access.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/print_string.h" @@ -1953,6 +1954,38 @@ bool SceneTree::is_using_font_oversampling() const { return use_font_oversampling; } +void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + + if (p_function == "change_scene") { + DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); + List<String> directories; + directories.push_back(dir_access->get_current_dir()); + + while (!directories.empty()) { + dir_access->change_dir(directories.back()->get()); + directories.pop_back(); + + dir_access->list_dir_begin(); + String filename = dir_access->get_next(); + + while (filename != "") { + if (filename == "." || filename == "..") { + filename = dir_access->get_next(); + continue; + } + + if (dir_access->dir_exists(filename)) { + directories.push_back(dir_access->get_current_dir().plus_file(filename)); + } else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) { + r_options->push_back("\"" + dir_access->get_current_dir().plus_file(filename) + "\""); + } + + filename = dir_access->get_next(); + } + } + } +} + SceneTree::SceneTree() { if (singleton == NULL) singleton = this; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 42a87545a6..d387886d61 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -408,6 +408,7 @@ public: void drop_files(const Vector<String> &p_files, int p_from_screen = 0); void global_menu_action(const Variant &p_id, const Variant &p_meta); + void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; //network API diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 03d46fd28d..14cc705edb 100755 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -107,6 +107,9 @@ bool Timer::has_autostart() const { } void Timer::start(float p_time) { + + ERR_FAIL_COND_MSG(!is_inside_tree(), "Timer was not added to the SceneTree!"); + if (p_time > 0) { set_wait_time(p_time); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 93eea2ad0b..b5c82ce4e3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2289,32 +2289,34 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (from && p_event->is_pressed()) { Control *next = NULL; - if (p_event->is_action_pressed("ui_focus_next")) { + Input *input = Input::get_singleton(); + + if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { next = from->find_next_valid_focus(); } - if (p_event->is_action_pressed("ui_focus_prev")) { + if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) { next = from->find_prev_valid_focus(); } - if (!mods && p_event->is_action_pressed("ui_up")) { + if (!mods && p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) { next = from->_get_focus_neighbour(MARGIN_TOP); } - if (!mods && p_event->is_action_pressed("ui_left")) { + if (!mods && p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) { next = from->_get_focus_neighbour(MARGIN_LEFT); } - if (!mods && p_event->is_action_pressed("ui_right")) { + if (!mods && p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) { next = from->_get_focus_neighbour(MARGIN_RIGHT); } - if (!mods && p_event->is_action_pressed("ui_down")) { + if (!mods && p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) { next = from->_get_focus_neighbour(MARGIN_BOTTOM); } |