summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS.md2
-rw-r--r--DONORS.md92
-rw-r--r--core/math/math_funcs.h10
-rw-r--r--core/math/transform.h39
-rw-r--r--core/os/input_event.cpp4
-rw-r--r--core/script_debugger_local.cpp2
-rw-r--r--core/script_debugger_local.h2
-rw-r--r--core/script_debugger_remote.cpp15
-rw-r--r--core/script_debugger_remote.h6
-rw-r--r--core/script_language.cpp52
-rw-r--r--core/script_language.h8
-rw-r--r--doc/classes/Button.xml3
-rw-r--r--doc/classes/EditorVCSInterface.xml115
-rw-r--r--doc/classes/GraphNode.xml1
-rw-r--r--editor/animation_bezier_editor.cpp16
-rw-r--r--editor/editor_node.cpp34
-rw-r--r--editor/editor_node.h6
-rw-r--r--editor/editor_plugin.cpp5
-rw-r--r--editor/editor_plugin.h1
-rw-r--r--editor/editor_properties.cpp11
-rw-r--r--editor/editor_properties.h1
-rw-r--r--editor/editor_resource_preview.cpp3
-rw-r--r--editor/editor_run.cpp7
-rw-r--r--editor/editor_run.h2
-rw-r--r--editor/editor_spin_slider.cpp29
-rw-r--r--editor/editor_spin_slider.h1
-rw-r--r--editor/editor_vcs_interface.cpp203
-rw-r--r--editor/editor_vcs_interface.h83
-rw-r--r--editor/icons/icon_auto_key.svg57
-rw-r--r--editor/icons/icon_debug_skip_breakpoints_off.svg89
-rw-r--r--editor/icons/icon_debug_skip_breakpoints_on.svg106
-rw-r--r--editor/inspector_dock.cpp4
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp30
-rw-r--r--editor/plugins/asset_library_editor_plugin.h7
-rw-r--r--editor/plugins/script_text_editor.cpp7
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp595
-rw-r--r--editor/plugins/version_control_editor_plugin.h146
-rw-r--r--editor/property_editor.cpp7
-rw-r--r--editor/scene_tree_dock.cpp11
-rw-r--r--editor/scene_tree_editor.cpp28
-rw-r--r--editor/script_editor_debugger.cpp29
-rw-r--r--editor/script_editor_debugger.h6
-rw-r--r--main/main.cpp5
-rw-r--r--misc/dist/html/full-size.html4
-rw-r--r--modules/assimp/import_state.h2
-rw-r--r--modules/gdscript/gdscript_editor.cpp20
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/glue/Managed/Files/Mathf.cs5
-rw-r--r--modules/mono/glue/Managed/Files/MathfEx.cs7
-rw-r--r--modules/visual_script/visual_script.cpp4
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp32
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h1
-rw-r--r--scene/2d/physics_body_2d.cpp8
-rw-r--r--scene/3d/physics_body.cpp12
-rw-r--r--scene/3d/physics_body.h1
-rw-r--r--scene/animation/tween.cpp4
-rw-r--r--scene/gui/button.cpp287
-rw-r--r--scene/gui/button.h6
-rw-r--r--scene/gui/color_picker.cpp2
-rw-r--r--scene/gui/control.cpp4
-rw-r--r--scene/gui/line_edit.cpp28
-rw-r--r--scene/gui/rich_text_effect.cpp122
-rw-r--r--scene/gui/rich_text_effect.h87
-rw-r--r--scene/gui/rich_text_label.cpp464
-rw-r--r--scene/gui/rich_text_label.h150
-rw-r--r--scene/gui/text_edit.cpp27
-rw-r--r--scene/gui/texture_button.cpp16
-rw-r--r--scene/gui/tree.cpp19
-rw-r--r--scene/main/node.cpp24
-rw-r--r--scene/main/node.h5
-rwxr-xr-xscene/main/timer.cpp3
-rw-r--r--scene/main/viewport.cpp14
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/mesh_data_tool.cpp3
74 files changed, 2879 insertions, 365 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)
diff --git a/DONORS.md b/DONORS.md
index 6c4b9b0514..ce58721697 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -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_debugger_local.cpp b/core/script_debugger_local.cpp
index ac4dafcf59..e61f9f7158 100644
--- a/core/script_debugger_local.cpp
+++ b/core/script_debugger_local.cpp
@@ -33,7 +33,7 @@
#include "core/os/os.h"
#include "scene/main/scene_tree.h"
-void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) {
+void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
if (!target_function.empty()) {
String current_function = p_script->debug_get_stack_level_function(0);
diff --git a/core/script_debugger_local.h b/core/script_debugger_local.h
index 19151d4cb0..b3aed5e358 100644
--- a/core/script_debugger_local.h
+++ b/core/script_debugger_local.h
@@ -48,7 +48,7 @@ class ScriptDebuggerLocal : public ScriptDebugger {
void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix);
public:
- void debug(ScriptLanguage *p_script, bool p_can_continue);
+ void debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint);
virtual void send_message(const String &p_message, const Array &p_args);
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp
index 2a061f0947..0b2b7368d2 100644
--- a/core/script_debugger_remote.cpp
+++ b/core/script_debugger_remote.cpp
@@ -129,11 +129,14 @@ void ScriptDebuggerRemote::_save_node(ObjectID id, const String &p_path) {
ResourceSaver::save(p_path, ps);
}
-void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue) {
+void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue, bool p_is_error_breakpoint) {
//this function is called when there is a debugger break (bug on script)
//or when execution is paused from editor
+ if (skip_breakpoints && !p_is_error_breakpoint)
+ return;
+
ERR_FAIL_COND_MSG(!tcp_client->is_connected_to_host(), "Script Debugger failed to connect, but being used anyway.");
packet_peer_stream->put_var("debug_enter");
@@ -155,6 +158,7 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
Variant var;
Error err = packet_peer_stream->get_var(var);
+
ERR_CONTINUE(err != OK);
ERR_CONTINUE(var.get_type() != Variant::ARRAY);
@@ -266,7 +270,6 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
break;
} else if (command == "continue") {
-
set_depth(-1);
set_lines_left(-1);
OS::get_singleton()->move_window_to_foreground();
@@ -302,6 +305,8 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue)
} else if (command == "save_node") {
_save_node(cmd[1], cmd[2]);
+ } else if (command == "set_skip_breakpoints") {
+ skip_breakpoints = cmd[1];
} else {
_parse_live_edit(cmd);
}
@@ -773,6 +778,8 @@ void ScriptDebuggerRemote::_poll_events() {
insert_breakpoint(cmd[2], cmd[1]);
else
remove_breakpoint(cmd[2], cmd[1]);
+ } else if (command == "set_skip_breakpoints") {
+ skip_breakpoints = cmd[1];
} else {
_parse_live_edit(cmd);
}
@@ -1102,6 +1109,10 @@ void ScriptDebuggerRemote::profiling_set_frame_times(float p_frame_time, float p
physics_frame_time = p_physics_frame_time;
}
+void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
+ skip_breakpoints = p_skip_breakpoints;
+}
+
ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
ScriptDebuggerRemote::ScriptDebuggerRemote() :
diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h
index a5bfd7a32d..34585afc80 100644
--- a/core/script_debugger_remote.h
+++ b/core/script_debugger_remote.h
@@ -140,6 +140,8 @@ class ScriptDebuggerRemote : public ScriptDebugger {
void _save_node(ObjectID id, const String &p_path);
+ bool skip_breakpoints;
+
public:
struct ResourceUsage {
@@ -156,7 +158,7 @@ public:
static ResourceUsageFunc resource_usage_func;
Error connect_to_host(const String &p_host, uint16_t p_port);
- virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true);
+ virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
virtual void idle_poll();
virtual void line_poll();
@@ -176,6 +178,8 @@ public:
virtual void profiling_end();
virtual void profiling_set_frame_times(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time);
+ virtual void set_skip_breakpoints(bool p_skip_breakpoints);
+
ScriptDebuggerRemote();
~ScriptDebuggerRemote();
};
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..5c7e8bb36c 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;
@@ -461,7 +467,7 @@ public:
void clear_breakpoints();
const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; }
- virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true) = 0;
+ virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0;
virtual void idle_poll();
virtual void line_poll();
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 b3810ac3cf..a1998a1d7c 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -123,6 +123,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"
@@ -185,6 +186,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");
@@ -1836,6 +1851,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
String main_scene;
String run_filename;
String args;
+ bool skip_breakpoints;
if (p_current || (editor_data.get_edited_scene_root() && p_custom == editor_data.get_edited_scene_root()->get_filename())) {
@@ -1901,8 +1917,9 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
editor_data.get_editor_breakpoints(&breakpoints);
args = ProjectSettings::get_singleton()->get("editor/main_run_args");
+ skip_breakpoints = ScriptEditor::get_singleton()->get_debugger()->is_skip_breakpoints();
- Error error = editor_run.run(run_filename, args, breakpoints);
+ Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints);
if (error != OK) {
@@ -3524,6 +3541,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>();
@@ -5313,6 +5331,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);
@@ -5642,6 +5661,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);
@@ -6001,6 +6022,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);
@@ -6010,7 +6040,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");
@@ -6475,6 +6504,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_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 4a7a9fb863..5c15ebb1eb 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -295,8 +295,9 @@ void EditorResourcePreview::_thread() {
//update modified time
f = FileAccess::open(file, FileAccess::WRITE);
- f->store_line(itos(modtime));
+ f->store_line(itos(thumbnail_size));
f->store_line(itos(has_small_texture));
+ f->store_line(itos(modtime));
f->store_line(md5);
memdelete(f);
}
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index e4e32b2ce0..7b5ec9e772 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -37,7 +37,8 @@ EditorRun::Status EditorRun::get_status() const {
return status;
}
-Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints) {
+
+Error EditorRun::run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints) {
List<String> args;
@@ -162,6 +163,10 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
args.push_back(bpoints);
}
+ if (p_skip_breakpoints) {
+ args.push_back("--skip-breakpoints");
+ }
+
if (p_scene != "") {
args.push_back(p_scene);
}
diff --git a/editor/editor_run.h b/editor/editor_run.h
index 9127c62030..42724630b5 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -51,7 +51,7 @@ private:
public:
Status get_status() const;
- Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints);
+ Error run(const String &p_scene, const String &p_custom_args, const List<String> &p_breakpoints, const bool &p_skip_breakpoints = false);
void run_native_notify() { status = STATUS_PLAY; }
void stop();
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..8da1777871
--- /dev/null
+++ b/editor/editor_vcs_interface.cpp
@@ -0,0 +1,203 @@
+/*************************************************************************/
+/* editor_vcs_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "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..896193ed92
--- /dev/null
+++ b/editor/editor_vcs_interface.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* editor_vcs_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef 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/icons/icon_debug_skip_breakpoints_off.svg b/editor/icons/icon_debug_skip_breakpoints_off.svg
new file mode 100644
index 0000000000..2a0b949aa9
--- /dev/null
+++ b/editor/icons/icon_debug_skip_breakpoints_off.svg
@@ -0,0 +1,89 @@
+<?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="17"
+ height="17"
+ version="1.1"
+ viewBox="0 0 17 17"
+ id="svg3801"
+ sodipodi:docname="icon_debug_skip_breakpoints_off.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata3807">
+ <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 />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs3805" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1887"
+ inkscape:window-height="2103"
+ id="namedview3803"
+ showgrid="false"
+ inkscape:measure-start="15.5563,28.7373"
+ inkscape:measure-end="0,0"
+ inkscape:zoom="32"
+ inkscape:cx="8.5156226"
+ inkscape:cy="-9.0970543"
+ inkscape:window-x="1953"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg3801" />
+ <path
+ id="path3835"
+ sodipodi:type="arc"
+ sodipodi:cx="3.2790968"
+ sodipodi:cy="3.006855"
+ sodipodi:rx="1.6192284"
+ sodipodi:ry="1.3289529"
+ sodipodi:start="0"
+ sodipodi:end="0.0073006075"
+ sodipodi:open="true"
+ d="m 4.8983252,3.006855 a 1.6192284,1.3289529 0 0 1 -4.31e-5,0.0097"
+ style="fill:#000000;stroke-width:0.62631863" />
+ <path
+ style="fill:#000000;stroke-width:0.62631863"
+ id="path3837"
+ sodipodi:type="arc"
+ sodipodi:cx="6.233613"
+ sodipodi:cy="5.0553513"
+ sodipodi:rx="2.563139"
+ sodipodi:ry="3.6270869"
+ sodipodi:start="0"
+ sodipodi:end="0.0073006075"
+ sodipodi:open="true"
+ d="m 8.796752,5.0553513 a 2.563139,3.6270869 0 0 1 -6.83e-5,0.02648" />
+ <path
+ style="fill:#ff8585;fill-opacity:0.99607843;stroke-width:1.01912296"
+ id="path3839"
+ sodipodi:type="arc"
+ sodipodi:cx="8.4689026"
+ sodipodi:cy="8.479969"
+ sodipodi:rx="6.1594577"
+ sodipodi:ry="6.0545759"
+ sodipodi:start="5.5685493"
+ sodipodi:end="5.2409356"
+ d="M 13.121337,4.512148 A 6.1594577,6.0545759 0 0 1 12.87255,12.713238 6.1594577,6.0545759 0 0 1 4.5370096,13.140453 6.1594577,6.0545759 0 0 1 3.4219038,5.0092664 6.1594577,6.0545759 0 0 1 11.574987,3.2515951"
+ sodipodi:open="true" />
+</svg>
diff --git a/editor/icons/icon_debug_skip_breakpoints_on.svg b/editor/icons/icon_debug_skip_breakpoints_on.svg
new file mode 100644
index 0000000000..186778a1b4
--- /dev/null
+++ b/editor/icons/icon_debug_skip_breakpoints_on.svg
@@ -0,0 +1,106 @@
+<?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="17"
+ height="17"
+ version="1.1"
+ viewBox="0 0 17 17"
+ id="svg3801"
+ sodipodi:docname="icon_debug_skip_breakpoints_on.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata3807">
+ <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="defs3805" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1887"
+ inkscape:window-height="2105"
+ id="namedview3803"
+ showgrid="false"
+ inkscape:measure-start="15.5563,28.7373"
+ inkscape:measure-end="0,0"
+ inkscape:zoom="40.96"
+ inkscape:cx="12.825686"
+ inkscape:cy="-5.1961973"
+ inkscape:window-x="1953"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="layer1" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="bg" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="fg">
+ <path
+ id="path3835"
+ sodipodi:type="arc"
+ sodipodi:cx="3.2790968"
+ sodipodi:cy="3.006855"
+ sodipodi:rx="1.6192284"
+ sodipodi:ry="1.3289529"
+ sodipodi:start="0"
+ sodipodi:end="0.0073006075"
+ sodipodi:open="true"
+ d="m 4.8983252,3.006855 a 1.6192284,1.3289529 0 0 1 -4.31e-5,0.0097"
+ style="fill:#000000;stroke-width:0.62631863" />
+ <path
+ style="fill:#000000;stroke-width:0.62631863"
+ id="path3837"
+ sodipodi:type="arc"
+ sodipodi:cx="6.233613"
+ sodipodi:cy="5.0553513"
+ sodipodi:rx="2.563139"
+ sodipodi:ry="3.6270869"
+ sodipodi:start="0"
+ sodipodi:end="0.0073006075"
+ sodipodi:open="true"
+ d="m 8.796752,5.0553513 a 2.563139,3.6270869 0 0 1 -6.83e-5,0.02648" />
+ <path
+ style="fill:#ff8585;fill-opacity:0.99607843;stroke-width:1.01912296"
+ id="path3839"
+ sodipodi:type="arc"
+ sodipodi:cx="8.4689026"
+ sodipodi:cy="8.479969"
+ sodipodi:rx="6.1594577"
+ sodipodi:ry="6.0545759"
+ sodipodi:start="5.5685493"
+ sodipodi:end="5.2409356"
+ d="M 13.121337,4.512148 A 6.1594577,6.0545759 0 0 1 12.87255,12.713238 6.1594577,6.0545759 0 0 1 4.5370096,13.140453 6.1594577,6.0545759 0 0 1 3.4219038,5.0092664 6.1594577,6.0545759 0 0 1 11.574987,3.2515951"
+ sodipodi:open="true" />
+ <rect
+ style="fill:#e0e0e0;fill-opacity:1;stroke-width:1.1873318"
+ id="rect5375"
+ width="18.575495"
+ height="2.5187109"
+ x="-9.2906752"
+ y="10.816157"
+ transform="matrix(0.70605846,-0.70815355,0.70605846,0.70815355,0,0)" />
+ </g>
+</svg>
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 8a0812973f..8ba7d9fba7 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -408,6 +408,10 @@ void InspectorDock::update(Object *p_object) {
warning->hide();
search->set_editable(false);
+ editor_path->set_text("");
+ editor_path->set_tooltip("");
+ editor_path->set_icon(NULL);
+
return;
}
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 60b5f017d2..3d161dc5b9 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -578,7 +578,6 @@ void EditorAssetLibrary::_notification(int p_what) {
case NOTIFICATION_READY: {
error_tr->set_texture(get_icon("Error", "EditorIcons"));
- reverse->set_icon(get_icon("Sort", "EditorIcons"));
filter->set_right_icon(get_icon("Search", "EditorIcons"));
filter->set_clear_button_enabled(true);
@@ -612,7 +611,6 @@ void EditorAssetLibrary::_notification(int p_what) {
library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree"));
downloads_scroll->add_style_override("bg", get_stylebox("bg", "Tree"));
error_tr->set_texture(get_icon("Error", "EditorIcons"));
- reverse->set_icon(get_icon("Sort", "EditorIcons"));
filter->set_right_icon(get_icon("Search", "EditorIcons"));
filter->set_clear_button_enabled(true);
} break;
@@ -645,23 +643,27 @@ void EditorAssetLibrary::_install_asset() {
}
const char *EditorAssetLibrary::sort_key[SORT_MAX] = {
- "downloads",
+ "updated",
+ "updated",
"name",
+ "name",
+ "cost",
"cost",
- "updated"
};
const char *EditorAssetLibrary::sort_text[SORT_MAX] = {
- "Downloads",
- "Name",
- "License", // "cost" stores the SPDX license name in the Godot Asset Library.
- "Updated"
+ "Recently Updated",
+ "Least Recently Updated",
+ "Name (A-Z)",
+ "Name (Z-A)",
+ "License (A-Z)", // "cost" stores the SPDX license name in the Godot Asset Library.
+ "License (Z-A)", // "cost" stores the SPDX license name in the Godot Asset Library.
};
const char *EditorAssetLibrary::support_key[SUPPORT_MAX] = {
"official",
"community",
- "testing"
+ "testing",
};
void EditorAssetLibrary::_select_author(int p_id) {
@@ -928,8 +930,8 @@ void EditorAssetLibrary::_search(int p_page) {
args += "&category=" + itos(categories->get_item_metadata(categories->get_selected()));
}
- if (reverse->is_pressed()) {
-
+ // Sorting options with an odd index are always the reverse of the previous one
+ if (sort->get_selected() % 2 == 1) {
args += "&reverse=true";
}
@@ -1381,12 +1383,6 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
sort->set_h_size_flags(SIZE_EXPAND_FILL);
sort->connect("item_selected", this, "_rerun_search");
- reverse = memnew(ToolButton);
- reverse->set_toggle_mode(true);
- reverse->connect("toggled", this, "_rerun_search");
- reverse->set_tooltip(TTR("Reverse sorting."));
- search_hb2->add_child(reverse);
-
search_hb2->add_child(memnew(VSeparator));
search_hb2->add_child(memnew(Label(TTR("Category:") + " ")));
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 6a3158889e..7e934ac6cb 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -190,7 +190,6 @@ class EditorAssetLibrary : public PanelContainer {
OptionButton *categories;
OptionButton *repository;
OptionButton *sort;
- ToolButton *reverse;
Button *search;
HBoxContainer *error_hb;
TextureRect *error_tr;
@@ -216,10 +215,12 @@ class EditorAssetLibrary : public PanelContainer {
};
enum SortOrder {
- SORT_DOWNLOADS,
+ SORT_UPDATED,
+ SORT_UPDATED_REVERSE,
SORT_NAME,
+ SORT_NAME_REVERSE,
SORT_COST,
- SORT_UPDATED,
+ SORT_COST_REVERSE,
SORT_MAX
};
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..c059977487
--- /dev/null
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -0,0 +1,595 @@
+/*************************************************************************/
+/* version_control_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "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..450ebccce1
--- /dev/null
+++ b/editor/plugins/version_control_editor_plugin.h
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* version_control_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef 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/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index fc5aecdbe9..a4270786c1 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -201,6 +201,21 @@ void ScriptEditorDebugger::debug_copy() {
OS::get_singleton()->set_clipboard(msg);
}
+void ScriptEditorDebugger::debug_skip_breakpoints() {
+ skip_breakpoints_value = !skip_breakpoints_value;
+ if (skip_breakpoints_value)
+ skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOn", "EditorIcons"));
+ else
+ skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOff", "EditorIcons"));
+
+ if (connection.is_valid()) {
+ Array msg;
+ msg.push_back("set_skip_breakpoints");
+ msg.push_back(skip_breakpoints_value);
+ ppeer->put_var(msg);
+ }
+}
+
void ScriptEditorDebugger::debug_next() {
ERR_FAIL_COND(!breaked);
@@ -1083,7 +1098,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
inspector->edit(variables);
-
+ skip_breakpoints->set_icon(get_icon("DebugSkipBreakpointsOff", "EditorIcons"));
copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
step->set_icon(get_icon("DebugStep", "EditorIcons"));
@@ -1785,6 +1800,10 @@ void ScriptEditorDebugger::reload_scripts() {
}
}
+bool ScriptEditorDebugger::is_skip_breakpoints() {
+ return skip_breakpoints_value;
+}
+
void ScriptEditorDebugger::_error_activated() {
TreeItem *selected = error_tree->get_selected();
@@ -1980,6 +1999,7 @@ void ScriptEditorDebugger::_bind_methods() {
ClassDB::bind_method(D_METHOD("_stack_dump_frame_selected"), &ScriptEditorDebugger::_stack_dump_frame_selected);
+ ClassDB::bind_method(D_METHOD("debug_skip_breakpoints"), &ScriptEditorDebugger::debug_skip_breakpoints);
ClassDB::bind_method(D_METHOD("debug_copy"), &ScriptEditorDebugger::debug_copy);
ClassDB::bind_method(D_METHOD("debug_next"), &ScriptEditorDebugger::debug_next);
@@ -2067,6 +2087,13 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
hbc->add_child(memnew(VSeparator));
+ skip_breakpoints = memnew(ToolButton);
+ hbc->add_child(skip_breakpoints);
+ skip_breakpoints->set_tooltip(TTR("Skip Breakpoints"));
+ skip_breakpoints->connect("pressed", this, "debug_skip_breakpoints");
+
+ hbc->add_child(memnew(VSeparator));
+
copy = memnew(ToolButton);
hbc->add_child(copy);
copy->set_tooltip(TTR("Copy Error"));
diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h
index 947b0cca52..dc9c99591d 100644
--- a/editor/script_editor_debugger.h
+++ b/editor/script_editor_debugger.h
@@ -109,12 +109,15 @@ class ScriptEditorDebugger : public Control {
bool hide_on_stop;
bool enable_external_editor;
+
+ bool skip_breakpoints_value = false;
Ref<Script> stack_script;
TabContainer *tabs;
Label *reason;
+ Button *skip_breakpoints;
Button *copy;
Button *step;
Button *next;
@@ -219,6 +222,7 @@ public:
void unpause();
void stop();
+ void debug_skip_breakpoints();
void debug_copy();
void debug_next();
@@ -256,6 +260,8 @@ public:
void reload_scripts();
+ bool is_skip_breakpoints();
+
virtual Size2 get_minimum_size() const;
ScriptEditorDebugger(EditorNode *p_editor = NULL);
~ScriptEditorDebugger();
diff --git a/main/main.cpp b/main/main.cpp
index f4665c4ad4..9ae885a5ce 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -386,6 +386,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
bool upwards = false;
String debug_mode;
String debug_host;
+ bool skip_breakpoints = false;
String main_pack;
bool quiet_stdout = false;
int rtm = -1;
@@ -737,6 +738,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
print_fps = true;
} else if (I->get() == "--disable-crash-handler") {
OS::get_singleton()->disable_crash_handler();
+ } else if (I->get() == "--skip-breakpoints") {
+ skip_breakpoints = true;
} else {
main_args.push_back(I->get());
}
@@ -806,6 +809,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
Error derr = sdr->connect_to_host(debug_host, debug_port);
+ sdr->set_skip_breakpoints(skip_breakpoints);
+
if (derr != OK) {
memdelete(sdr);
} else {
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..9b3bf8ad5b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -223,7 +223,7 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
_debug_parse_err_line = p_line;
_debug_parse_err_file = p_file;
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, false);
+ ScriptDebugger::get_singleton()->debug(this, false, true);
return true;
} else {
return false;
@@ -237,7 +237,8 @@ bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue)
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, p_allow_continue);
+ bool is_error_breakpoint = p_error != "Breakpoint";
+ ScriptDebugger::get_singleton()->debug(this, p_allow_continue, is_error_breakpoint);
return true;
} else {
return false;
@@ -1936,9 +1937,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/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 4c9dd9c1a9..e14e919f92 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1101,7 +1101,7 @@ bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const S
_debug_parse_err_line = p_line;
_debug_parse_err_file = p_file;
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, false);
+ ScriptDebugger::get_singleton()->debug(this, false, true);
return true;
} else {
return false;
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/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 3f8b2b1831..70389b6729 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2450,7 +2450,7 @@ bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, c
_debug_parse_err_node = p_node;
_debug_parse_err_file = p_file;
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, false);
+ ScriptDebugger::get_singleton()->debug(this, false, true);
return true;
} else {
return false;
@@ -2464,7 +2464,7 @@ bool VisualScriptLanguage::debug_break(const String &p_error, bool p_allow_conti
_debug_parse_err_node = -1;
_debug_parse_err_file = "";
_debug_error = p_error;
- ScriptDebugger::get_singleton()->debug(this, p_allow_continue);
+ ScriptDebugger::get_singleton()->debug(this, p_allow_continue, true);
return true;
} else {
return false;
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 8088a71198..3fdceacebb 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -106,6 +106,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"smoothstep",
"posmod",
"lerp_angle",
+ "ord",
};
VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String &p_string) {
@@ -181,6 +182,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case OBJ_WEAKREF:
case TYPE_OF:
case TEXT_CHAR:
+ case TEXT_ORD:
case TEXT_STR:
case TEXT_PRINT:
case TEXT_PRINTERR:
@@ -438,6 +440,9 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case TYPE_EXISTS: {
return PropertyInfo(Variant::STRING, "type");
} break;
+ case TEXT_ORD: {
+ return PropertyInfo(Variant::STRING, "character");
+ } break;
case TEXT_CHAR: {
return PropertyInfo(Variant::INT, "ascii");
} break;
@@ -594,6 +599,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case TYPE_CONVERT: {
} break;
+ case TEXT_ORD:
case TYPE_OF: {
t = Variant::INT;
@@ -1132,6 +1138,30 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
*r_return = String(result);
} break;
+ case VisualScriptBuiltinFunc::TEXT_ORD: {
+
+ if (p_inputs[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+
+ return;
+ }
+
+ String str = p_inputs[0]->operator String();
+
+ if (str.length() != 1) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ *r_return = "Expected a string of length 1 (a character).";
+
+ return;
+ }
+
+ *r_return = str.get(0);
+
+ } break;
case VisualScriptBuiltinFunc::TEXT_STR: {
String str = *p_inputs[0];
@@ -1374,6 +1404,7 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_SMOOTHSTEP);
BIND_ENUM_CONSTANT(MATH_POSMOD);
BIND_ENUM_CONSTANT(MATH_LERP_ANGLE);
+ BIND_ENUM_CONSTANT(TEXT_ORD);
BIND_ENUM_CONSTANT(FUNC_MAX);
}
@@ -1460,6 +1491,7 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/char", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_CHAR>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/ord", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_ORD>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/str", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/print", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index cf475d675d..998a6cbc6a 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -106,6 +106,7 @@ public:
MATH_SMOOTHSTEP,
MATH_POSMOD,
MATH_LERP_ANGLE,
+ TEXT_ORD,
FUNC_MAX
};
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/line_edit.cpp b/scene/gui/line_edit.cpp
index 4a763844f8..6893c38733 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -872,7 +872,9 @@ void LineEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if (!caret_blink_enabled) {
+ if (caret_blink_enabled) {
+ caret_blink_timer->start();
+ } else {
draw_caret = true;
}
@@ -886,6 +888,10 @@ void LineEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_EXIT: {
+ if (caret_blink_enabled) {
+ caret_blink_timer->stop();
+ }
+
OS::get_singleton()->set_ime_position(Point2());
OS::get_singleton()->set_ime_active(false);
ime_text = "";
@@ -1053,11 +1059,15 @@ bool LineEdit::cursor_get_blink_enabled() const {
void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
- if (p_enabled) {
- caret_blink_timer->start();
- } else {
- caret_blink_timer->stop();
+
+ if (has_focus()) {
+ if (p_enabled) {
+ caret_blink_timer->start();
+ } else {
+ caret_blink_timer->stop();
+ }
}
+
draw_caret = true;
}
@@ -1072,10 +1082,12 @@ void LineEdit::cursor_set_blink_speed(const float p_speed) {
void LineEdit::_reset_caret_blink_timer() {
if (caret_blink_enabled) {
- caret_blink_timer->stop();
- caret_blink_timer->start();
draw_caret = true;
- update();
+ if (has_focus()) {
+ caret_blink_timer->stop();
+ caret_blink_timer->start();
+ update();
+ }
}
}
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
new file mode 100644
index 0000000000..8d9b8ad1e0
--- /dev/null
+++ b/scene/gui/rich_text_effect.cpp
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* rich_text_effect.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "rich_text_effect.h"
+
+#include "core/script_language.h"
+
+void RichTextEffect::_bind_methods() {
+ BIND_VMETHOD(MethodInfo(Variant::INT, "_process_custom_fx", PropertyInfo(Variant::OBJECT, "char_fx", PROPERTY_HINT_RESOURCE_TYPE, "CustomFXChar")));
+}
+
+Variant RichTextEffect::get_bbcode() const {
+ Variant r;
+ if (get_script_instance()) {
+ if (!get_script_instance()->get("bbcode", r)) {
+ String path = get_script_instance()->get_script()->get_path();
+ r = path.get_file().get_basename();
+ }
+ }
+ return r;
+}
+
+bool RichTextEffect::_process_effect_impl(Ref<CharFXTransform> p_cfx) {
+ bool return_value = false;
+ if (get_script_instance()) {
+ Variant v = get_script_instance()->call("_process_custom_fx", p_cfx);
+ if (v.get_type() != Variant::BOOL) {
+ return_value = false;
+ } else {
+ return_value = (bool)v;
+ }
+ }
+ return return_value;
+}
+
+RichTextEffect::RichTextEffect() {
+}
+
+void CharFXTransform::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index);
+ ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index);
+
+ ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index);
+ ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index);
+
+ ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time);
+ ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time);
+
+ ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible);
+ ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility);
+
+ ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset);
+ ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset);
+
+ ClassDB::bind_method(D_METHOD("get_color"), &CharFXTransform::get_color);
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &CharFXTransform::set_color);
+
+ ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment);
+ ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment);
+
+ ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character);
+ ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character);
+
+ ClassDB::bind_method(D_METHOD("get_value_or", "key", "default_value"), &CharFXTransform::get_value_or);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "elapsed_time"), "set_elapsed_time", "get_elapsed_time");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character");
+}
+
+Variant CharFXTransform::get_value_or(String p_key, Variant p_default_value) {
+ if (!this->environment.has(p_key))
+ return p_default_value;
+
+ Variant r = environment[p_key];
+ if (r.get_type() != p_default_value.get_type())
+ return p_default_value;
+
+ return r;
+}
+
+CharFXTransform::CharFXTransform() {
+ relative_index = 0;
+ absolute_index = 0;
+ visibility = true;
+ offset = Point2();
+ color = Color();
+ character = 0;
+}
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
new file mode 100644
index 0000000000..f9c3e15399
--- /dev/null
+++ b/scene/gui/rich_text_effect.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* rich_text_effect.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RICH_TEXT_EFFECT_H
+#define RICH_TEXT_EFFECT_H
+
+#include "core/resource.h"
+
+class RichTextEffect : public Resource {
+ GDCLASS(RichTextEffect, Resource);
+ OBJ_SAVE_TYPE(RichTextEffect);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Variant get_bbcode() const;
+ bool _process_effect_impl(Ref<class CharFXTransform> p_cfx);
+
+ RichTextEffect();
+};
+
+class CharFXTransform : public Reference {
+ GDCLASS(CharFXTransform, Reference);
+
+protected:
+ static void _bind_methods();
+
+public:
+ uint64_t relative_index;
+ uint64_t absolute_index;
+ bool visibility;
+ Point2 offset;
+ Color color;
+ CharType character;
+ float elapsed_time;
+ Dictionary environment;
+
+ CharFXTransform();
+ uint64_t get_relative_index() { return relative_index; }
+ void set_relative_index(uint64_t p_index) { relative_index = p_index; }
+ uint64_t get_absolute_index() { return absolute_index; }
+ void set_absolute_index(uint64_t p_index) { absolute_index = p_index; }
+ float get_elapsed_time() { return elapsed_time; }
+ void set_elapsed_time(float p_elapsed_time) { elapsed_time = p_elapsed_time; }
+ bool is_visible() { return visibility; }
+ void set_visibility(bool p_vis) { visibility = p_vis; }
+ Point2 get_offset() { return offset; }
+ void set_offset(Point2 p_offset) { offset = p_offset; }
+ Color get_color() { return color; }
+ void set_color(Color p_color) { color = p_color; }
+ int get_character() { return (int)character; }
+ void set_character(int p_char) { character = (CharType)p_char; }
+ Dictionary get_environment() { return environment; }
+ void set_environment(Dictionary p_environment) { environment = p_environment; }
+
+ Variant get_value_or(String p_key, Variant p_default_value);
+};
+
+#endif // RICH_TEXT_EFFECT_H
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 1aed858c94..77d5c8e91f 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -30,10 +30,11 @@
#include "rich_text_label.h"
+#include "core/math/math_defs.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "modules/regex/regex.h"
#include "scene/scene_string_names.h"
-
#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
#endif
@@ -139,6 +140,7 @@ Rect2 RichTextLabel::_get_text_rect() {
Ref<StyleBox> style = get_stylebox("normal");
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
}
+
int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
RID ci;
@@ -292,7 +294,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
Color selection_bg;
if (p_mode == PROCESS_DRAW) {
-
selection_fg = get_color("font_color_selected");
selection_bg = get_color("selection_color");
}
@@ -343,18 +344,24 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
Color font_color_shadow;
bool underline = false;
bool strikethrough = false;
+ ItemFade *fade = NULL;
+ int it_char_start = p_char_count;
+
+ Vector<ItemFX *> fx_stack = Vector<ItemFX *>();
+ bool custom_fx_ok = true;
if (p_mode == PROCESS_DRAW) {
color = _find_color(text, p_base_color);
font_color_shadow = _find_color(text, p_font_color_shadow);
if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) {
-
underline = true;
} else if (_find_strikethrough(text)) {
-
strikethrough = true;
}
+ fade = _fetch_by_type<ItemFade>(text, ITEM_FADE);
+ _fetch_item_stack<ItemFX>(text, fx_stack);
+
} else if (p_mode == PROCESS_CACHE) {
l.char_count += text->text.length();
}
@@ -431,8 +438,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
ofs += cw;
} else if (p_mode == PROCESS_DRAW) {
-
bool selected = false;
+ Color fx_color = Color(color);
+ Point2 fx_offset;
+ CharType fx_char = c[i];
+
if (selection.active) {
int cofs = (&c[i]) - cf;
@@ -442,8 +452,78 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
int cw = 0;
+ int c_item_offset = p_char_count - it_char_start;
+
+ float faded_visibility = 1.0f;
+ if (fade) {
+ if (c_item_offset >= fade->starting_index) {
+ faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length;
+ faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
+ }
+ fx_color.a = faded_visibility;
+ }
+
+ bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) &&
+ faded_visibility > 0.0f);
+
+ for (int j = 0; j < fx_stack.size(); j++) {
+ ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]);
+ ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]);
+ ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]);
+ ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]);
+ ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]);
+
+ if (item_custom && custom_fx_ok) {
+ Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform));
+ Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier);
+ if (!custom_effect.is_null()) {
+ charfx->elapsed_time = item_custom->elapsed_time;
+ charfx->environment = item_custom->environment;
+ charfx->relative_index = c_item_offset;
+ charfx->absolute_index = p_char_count;
+ charfx->visibility = visible;
+ charfx->offset = fx_offset;
+ charfx->color = fx_color;
+ charfx->character = fx_char;
+
+ bool effect_status = custom_effect->_process_effect_impl(charfx);
+ custom_fx_ok = effect_status;
+
+ fx_offset += charfx->offset;
+ fx_color = charfx->color;
+ visible &= charfx->visibility;
+ fx_char = charfx->character;
+ }
+ } else if (item_shake) {
+ uint64_t char_current_rand = item_shake->offset_random(c_item_offset);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
+ double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
+ n_time = (n_time > 1.0) ? 1.0 : n_time;
+ fx_offset += Point2(Math::lerp(Math::sin(previous_offset),
+ Math::sin(current_offset),
+ n_time),
+ Math::lerp(Math::cos(previous_offset),
+ Math::cos(current_offset),
+ n_time)) *
+ (float)item_shake->strength / 10.0f;
+ } else if (item_wave) {
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f);
+ fx_offset += Point2(0, 1) * value;
+ } else if (item_tornado) {
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
+ fx_offset += Point2(torn_x, torn_y);
+ } else if (item_rainbow) {
+ fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)),
+ item_rainbow->saturation,
+ item_rainbow->value,
+ fx_color.a);
+ }
+ }
- bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent));
if (visible)
line_is_blank = false;
@@ -451,27 +531,28 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
visible = false;
if (visible) {
+
if (selected) {
- cw = font->get_char_size(c[i], c[i + 1]).x;
+ cw = font->get_char_size(fx_char, c[i + 1]).x;
draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
}
if (p_font_color_shadow.a > 0) {
float x_ofs_shadow = align_ofs + pofs;
float y_ofs_shadow = y + lh - line_descent;
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, fx_char, c[i + 1], p_font_color_shadow);
if (p_shadow_as_outline) {
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
}
}
if (selected) {
- drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color);
} else {
- cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
+ cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color);
}
}
@@ -800,6 +881,31 @@ void RichTextLabel::_update_scroll() {
}
}
+void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) {
+ Item *it = p_frame;
+ while (it) {
+ ItemFX *ifx = Object::cast_to<ItemFX>(it);
+
+ if (!ifx) {
+ it = _get_next_item(it, true);
+ continue;
+ }
+
+ ifx->elapsed_time += p_delta_time;
+
+ ItemShake *shake = Object::cast_to<ItemShake>(it);
+ if (shake) {
+ bool cycle = (shake->elapsed_time > (1.0f / shake->rate));
+ if (cycle) {
+ shake->elapsed_time -= (1.0f / shake->rate);
+ shake->reroll_random();
+ }
+ }
+
+ it = _get_next_item(it, true);
+ }
+}
+
void RichTextLabel::_notification(int p_what) {
switch (p_what) {
@@ -873,6 +979,15 @@ void RichTextLabel::_notification(int p_what) {
from_line++;
}
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ float dt = get_process_delta_time();
+
+ for (int i = 0; i < custom_effects.size(); i++) {
+ }
+
+ _update_fx(main, dt);
+ update();
}
}
}
@@ -1026,15 +1141,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
}
if (b->get_button_index() == BUTTON_WHEEL_UP) {
-
if (scroll_active)
-
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
-
if (scroll_active)
-
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
}
}
@@ -1285,8 +1396,19 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) {
return false;
}
-bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
+bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) {
+ Item *item = p_item;
+ while (item) {
+ if (item->type == p_type) {
+ return true;
+ }
+ item = item->parent;
+ }
+ return false;
+}
+
+bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
Item *item = p_item;
while (item) {
@@ -1618,6 +1740,49 @@ void RichTextLabel::push_table(int p_columns) {
_add_item(item, true, true);
}
+void RichTextLabel::push_fade(int p_start_index, int p_length) {
+ ItemFade *item = memnew(ItemFade);
+ item->starting_index = p_start_index;
+ item->length = p_length;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
+ ItemShake *item = memnew(ItemShake);
+ item->strength = p_strength;
+ item->rate = p_rate;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
+ ItemWave *item = memnew(ItemWave);
+ item->frequency = p_frequency;
+ item->amplitude = p_amplitude;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
+ ItemTornado *item = memnew(ItemTornado);
+ item->frequency = p_frequency;
+ item->radius = p_radius;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) {
+ ItemRainbow *item = memnew(ItemRainbow);
+ item->frequency = p_frequency;
+ item->saturation = p_saturation;
+ item->value = p_value;
+ _add_item(item, true);
+}
+
+void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) {
+ ItemCustomFX *item = memnew(ItemCustomFX);
+ item->identifier = p_identifier;
+ item->environment = p_environment;
+ _add_item(item, true);
+}
+
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
ERR_FAIL_COND(current->type != ITEM_TABLE);
@@ -1762,6 +1927,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
bool in_bold = false;
bool in_italics = false;
+ set_process_internal(false);
+
while (pos < p_bbcode.length()) {
int brk_pos = p_bbcode.find("[", pos);
@@ -1785,7 +1952,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
}
String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
-
if (tag.begins_with("/") && tag_stack.size()) {
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
@@ -1798,9 +1964,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
indent_level--;
if (!tag_ok) {
-
- add_text("[");
- pos++;
+ add_text("[" + tag);
+ pos = brk_end;
continue;
}
@@ -1992,10 +2157,145 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("font");
- } else {
+ } else if (tag.begins_with("fade")) {
+ Vector<String> tags = tag.split(" ", false);
+ int startIndex = 0;
+ int length = 10;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("start=")) {
+ String start_str = expr.substr(6, expr.length());
+ startIndex = start_str.to_int();
+ } else if (expr.begins_with("length=")) {
+ String end_str = expr.substr(7, expr.length());
+ length = end_str.to_int();
+ }
+ }
+ }
+
+ push_fade(startIndex, length);
+ pos = brk_end + 1;
+ tag_stack.push_front("fade");
+ } else if (tag.begins_with("shake")) {
+ Vector<String> tags = tag.split(" ", false);
+ int strength = 5;
+ float rate = 20.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("level=")) {
+ String str_str = expr.substr(6, expr.length());
+ strength = str_str.to_int();
+ } else if (expr.begins_with("rate=")) {
+ String rate_str = expr.substr(5, expr.length());
+ rate = rate_str.to_float();
+ }
+ }
+ }
+
+ push_shake(strength, rate);
+ pos = brk_end + 1;
+ tag_stack.push_front("shake");
+ set_process_internal(true);
+ } else if (tag.begins_with("wave")) {
+ Vector<String> tags = tag.split(" ", false);
+ float amplitude = 20.0f;
+ float period = 5.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("amp=")) {
+ String amp_str = expr.substr(4, expr.length());
+ amplitude = amp_str.to_float();
+ } else if (expr.begins_with("freq=")) {
+ String period_str = expr.substr(5, expr.length());
+ period = period_str.to_float();
+ }
+ }
+ }
- add_text("["); //ignore
- pos = brk_pos + 1;
+ push_wave(period, amplitude);
+ pos = brk_end + 1;
+ tag_stack.push_front("wave");
+ set_process_internal(true);
+ } else if (tag.begins_with("tornado")) {
+ Vector<String> tags = tag.split(" ", false);
+ float radius = 10.0f;
+ float frequency = 1.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("radius=")) {
+ String amp_str = expr.substr(7, expr.length());
+ radius = amp_str.to_float();
+ } else if (expr.begins_with("freq=")) {
+ String period_str = expr.substr(5, expr.length());
+ frequency = period_str.to_float();
+ }
+ }
+ }
+
+ push_tornado(frequency, radius);
+ pos = brk_end + 1;
+ tag_stack.push_front("tornado");
+ set_process_internal(true);
+ } else if (tag.begins_with("rainbow")) {
+ Vector<String> tags = tag.split(" ", false);
+ float saturation = 0.8f;
+ float value = 0.8f;
+ float frequency = 1.0f;
+
+ if (tags.size() > 1) {
+ tags.remove(0);
+ for (int i = 0; i < tags.size(); i++) {
+ String expr = tags[i];
+ if (expr.begins_with("sat=")) {
+ String sat_str = expr.substr(4, expr.length());
+ saturation = sat_str.to_float();
+ } else if (expr.begins_with("val=")) {
+ String val_str = expr.substr(4, expr.length());
+ value = val_str.to_float();
+ } else if (expr.begins_with("freq=")) {
+ String freq_str = expr.substr(5, expr.length());
+ frequency = freq_str.to_float();
+ }
+ }
+ }
+
+ push_rainbow(saturation, value, frequency);
+ pos = brk_end + 1;
+ tag_stack.push_front("rainbow");
+ set_process_internal(true);
+ } else {
+ Vector<String> expr = tag.split(" ", false);
+ if (expr.size() < 1) {
+ add_text("[");
+ pos = brk_pos + 1;
+ } else {
+ String identifier = expr[0];
+ expr.remove(0);
+ Dictionary properties = parse_expressions_for_values(expr);
+ Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier);
+
+ if (!effect.is_null()) {
+ push_customfx(identifier, properties);
+ pos = brk_end + 1;
+ tag_stack.push_front(identifier);
+ set_process_internal(true);
+ } else {
+ add_text("["); //ignore
+ pos = brk_pos + 1;
+ }
+ }
}
}
@@ -2204,6 +2504,34 @@ float RichTextLabel::get_percent_visible() const {
return percent_visible;
}
+void RichTextLabel::set_effects(const Vector<Variant> &effects) {
+ custom_effects.clear();
+ for (int i = 0; i < effects.size(); i++) {
+ Ref<RichTextEffect> effect = Ref<RichTextEffect>(effects[i]);
+ custom_effects.push_back(effect);
+ }
+
+ parse_bbcode(bbcode);
+}
+
+Vector<Variant> RichTextLabel::get_effects() {
+ Vector<Variant> r;
+ for (int i = 0; i < custom_effects.size(); i++) {
+ r.push_back(custom_effects[i].get_ref_ptr());
+ }
+ return r;
+}
+
+void RichTextLabel::install_effect(const Variant effect) {
+ Ref<RichTextEffect> rteffect;
+ rteffect = effect;
+
+ if (rteffect.is_valid()) {
+ custom_effects.push_back(effect);
+ parse_bbcode(bbcode);
+ }
+}
+
int RichTextLabel::get_content_height() {
int total_height = 0;
if (main->lines.size())
@@ -2280,6 +2608,12 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
+ ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
+
+ ClassDB::bind_method(D_METHOD("set_effects", "effects"), &RichTextLabel::set_effects);
+ ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
+ ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
+
ADD_GROUP("BBCode", "bbcode_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode");
@@ -2297,6 +2631,8 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", (PropertyHint)(PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "17/17:RichTextEffect", PROPERTY_USAGE_DEFAULT, "RichTextEffect"), "set_effects", "get_effects");
+
ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
@@ -2322,11 +2658,16 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_INDENT);
BIND_ENUM_CONSTANT(ITEM_LIST);
BIND_ENUM_CONSTANT(ITEM_TABLE);
+ BIND_ENUM_CONSTANT(ITEM_FADE);
+ BIND_ENUM_CONSTANT(ITEM_SHAKE);
+ BIND_ENUM_CONSTANT(ITEM_WAVE);
+ BIND_ENUM_CONSTANT(ITEM_TORNADO);
+ BIND_ENUM_CONSTANT(ITEM_RAINBOW);
+ BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
BIND_ENUM_CONSTANT(ITEM_META);
}
void RichTextLabel::set_visible_characters(int p_visible) {
-
visible_characters = p_visible;
update();
}
@@ -2358,6 +2699,77 @@ Size2 RichTextLabel::get_minimum_size() const {
return Size2();
}
+Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
+ Ref<RichTextEffect> r;
+ for (int i = 0; i < custom_effects.size(); i++) {
+ if (!custom_effects[i].is_valid())
+ continue;
+
+ if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) {
+ r = custom_effects[i];
+ }
+ }
+
+ return r;
+}
+
+Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
+ Dictionary d = Dictionary();
+ for (int i = 0; i < p_expressions.size(); i++) {
+ String expression = p_expressions[i];
+
+ Array a = Array();
+ Vector<String> parts = expression.split("=", true);
+ String key = parts[0];
+ if (parts.size() != 2) {
+ return d;
+ }
+
+ Vector<String> values = parts[1].split(",", false);
+
+ RegEx color = RegEx();
+ color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
+ RegEx nodepath = RegEx();
+ nodepath.compile("^\\$");
+ RegEx boolean = RegEx();
+ boolean.compile("^(true|false)$");
+ RegEx decimal = RegEx();
+ decimal.compile("^-?^.?\\d+(\\.\\d+?)?$");
+ RegEx numerical = RegEx();
+ numerical.compile("^\\d+$");
+
+ for (int j = 0; j < values.size(); j++) {
+ if (!color.search(values[j]).is_null()) {
+ a.append(Color::html(values[j]));
+ } else if (!nodepath.search(values[j]).is_null()) {
+ if (values[j].begins_with("$")) {
+ String v = values[j].substr(1, values[j].length());
+ a.append(NodePath(v));
+ }
+ } else if (!boolean.search(values[j]).is_null()) {
+ if (values[j] == "true") {
+ a.append(true);
+ } else if (values[j] == "false") {
+ a.append(false);
+ }
+ } else if (!decimal.search(values[j]).is_null()) {
+ a.append(values[j].to_double());
+ } else if (!numerical.search(values[j]).is_null()) {
+ a.append(values[j].to_int());
+ } else {
+ a.append(values[j]);
+ }
+ }
+
+ if (values.size() > 1) {
+ d[key] = a;
+ } else if (values.size() == 1) {
+ d[key] = a[0];
+ }
+ }
+ return d;
+}
+
RichTextLabel::RichTextLabel() {
main = memnew(ItemFrame);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 21d099c37a..6755f9ef2a 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -31,6 +31,7 @@
#ifndef RICH_TEXT_LABEL_H
#define RICH_TEXT_LABEL_H
+#include "rich_text_effect.h"
#include "scene/gui/scroll_bar.h"
class RichTextLabel : public Control {
@@ -67,7 +68,13 @@ public:
ITEM_INDENT,
ITEM_LIST,
ITEM_TABLE,
- ITEM_META
+ ITEM_FADE,
+ ITEM_SHAKE,
+ ITEM_WAVE,
+ ITEM_TORNADO,
+ ITEM_RAINBOW,
+ ITEM_META,
+ ITEM_CUSTOMFX
};
protected:
@@ -96,7 +103,7 @@ private:
}
};
- struct Item {
+ struct Item : public Object {
int index;
Item *parent;
@@ -214,6 +221,101 @@ private:
ItemTable() { type = ITEM_TABLE; }
};
+ struct ItemFade : public Item {
+ int starting_index;
+ int length;
+
+ ItemFade() { type = ITEM_FADE; }
+ };
+
+ struct ItemFX : public Item {
+ float elapsed_time;
+
+ ItemFX() {
+ elapsed_time = 0.0f;
+ }
+ };
+
+ struct ItemShake : public ItemFX {
+ int strength;
+ float rate;
+ uint64_t _current_rng;
+ uint64_t _previous_rng;
+
+ ItemShake() {
+ strength = 0;
+ rate = 0.0f;
+ _current_rng = 0;
+ type = ITEM_SHAKE;
+ }
+
+ void reroll_random() {
+ _previous_rng = _current_rng;
+ _current_rng = Math::rand();
+ }
+
+ uint64_t offset_random(int index) {
+ return (_current_rng >> (index % 64)) |
+ (_current_rng << (64 - (index % 64)));
+ }
+
+ uint64_t offset_previous_random(int index) {
+ return (_previous_rng >> (index % 64)) |
+ (_previous_rng << (64 - (index % 64)));
+ }
+ };
+
+ struct ItemWave : public ItemFX {
+ float frequency;
+ float amplitude;
+
+ ItemWave() {
+ frequency = 1.0f;
+ amplitude = 1.0f;
+ type = ITEM_WAVE;
+ }
+ };
+
+ struct ItemTornado : public ItemFX {
+ float radius;
+ float frequency;
+
+ ItemTornado() {
+ radius = 1.0f;
+ frequency = 1.0f;
+ type = ITEM_TORNADO;
+ }
+ };
+
+ struct ItemRainbow : public ItemFX {
+ float saturation;
+ float value;
+ float frequency;
+
+ ItemRainbow() {
+ saturation = 0.8f;
+ value = 0.8f;
+ frequency = 1.0f;
+ type = ITEM_RAINBOW;
+ }
+ };
+
+ struct ItemCustomFX : public ItemFX {
+ String identifier;
+ Dictionary environment;
+
+ ItemCustomFX() {
+ identifier = "";
+ environment = Dictionary();
+ type = ITEM_CUSTOMFX;
+ }
+
+ virtual ~ItemCustomFX() {
+ _clear_children();
+ environment.clear();
+ }
+ };
+
ItemFrame *main;
Item *current;
ItemFrame *current_frame;
@@ -239,6 +341,8 @@ private:
ItemMeta *meta_hovering;
Variant current_meta;
+ Vector<Ref<RichTextEffect> > custom_effects;
+
void _invalidate_current_line(ItemFrame *p_frame);
void _validate_line_caches(ItemFrame *p_frame);
@@ -246,7 +350,6 @@ private:
void _remove_item(Item *p_item, const int p_line, const int p_subitem_line);
struct ProcessState {
-
int line_width;
};
@@ -287,8 +390,36 @@ private:
bool _find_strikethrough(Item *p_item);
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL);
bool _find_layout_subitem(Item *from, Item *to);
+ bool _find_by_type(Item *p_item, ItemType p_type);
+ template <typename T>
+ T *_fetch_by_type(Item *p_item, ItemType p_type) {
+ Item *item = p_item;
+ T *result = NULL;
+ while (item) {
+ if (item->type == p_type) {
+ result = Object::cast_to<T>(item);
+ if (result)
+ return result;
+ }
+ item = item->parent;
+ }
+
+ return result;
+ };
+ template <typename T>
+ void _fetch_item_stack(Item *p_item, Vector<T *> &r_stack) {
+ Item *item = p_item;
+ while (item) {
+ T *found = Object::cast_to<T>(item);
+ if (found) {
+ r_stack.push_back(found);
+ }
+ item = item->parent;
+ }
+ }
void _update_scroll();
+ void _update_fx(ItemFrame *p_frame, float p_delta_time);
void _scroll_changed(double);
void _gui_input(Ref<InputEvent> p_event);
@@ -296,6 +427,8 @@ private:
Item *_get_prev_item(Item *p_item, bool p_free = false);
Rect2 _get_text_rect();
+ Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier);
+ virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions);
bool use_bbcode;
String bbcode;
@@ -322,6 +455,12 @@ public:
void push_list(ListType p_list);
void push_meta(const Variant &p_meta);
void push_table(int p_columns);
+ void push_fade(int p_start_index, int p_length);
+ void push_shake(int p_level, float p_rate);
+ void push_wave(float p_frequency, float p_amplitude);
+ void push_tornado(float p_frequency, float p_radius);
+ void push_rainbow(float p_saturation, float p_value, float p_frequency);
+ void push_customfx(String p_identifier, Dictionary p_environment);
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
int get_current_table_column() const;
void push_cell();
@@ -380,6 +519,11 @@ public:
void set_percent_visible(float p_percent);
float get_percent_visible() const;
+ void set_effects(const Vector<Variant> &effects);
+ Vector<Variant> get_effects();
+
+ void install_effect(const Variant effect);
+
void set_fixed_size_to_width(int p_width);
virtual Size2 get_minimum_size() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 0464cc1ac8..7bcef5f9ab 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1738,7 +1738,9 @@ void TextEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if (!caret_blink_enabled) {
+ if (caret_blink_enabled) {
+ caret_blink_timer->start();
+ } else {
draw_caret = true;
}
@@ -1751,6 +1753,10 @@ void TextEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_EXIT: {
+ if (caret_blink_enabled) {
+ caret_blink_timer->stop();
+ }
+
OS::get_singleton()->set_ime_position(Point2());
OS::get_singleton()->set_ime_active(false);
ime_text = "";
@@ -4396,11 +4402,14 @@ bool TextEdit::cursor_get_blink_enabled() const {
void TextEdit::cursor_set_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
- if (p_enabled) {
- caret_blink_timer->start();
- } else {
- caret_blink_timer->stop();
+ if (has_focus()) {
+ if (p_enabled) {
+ caret_blink_timer->start();
+ } else {
+ caret_blink_timer->stop();
+ }
}
+
draw_caret = true;
}
@@ -4817,10 +4826,12 @@ int TextEdit::get_max_chars() const {
void TextEdit::_reset_caret_blink_timer() {
if (caret_blink_enabled) {
- caret_blink_timer->stop();
- caret_blink_timer->start();
draw_caret = true;
- update();
+ if (has_focus()) {
+ caret_blink_timer->stop();
+ caret_blink_timer->start();
+ update();
+ }
}
}
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/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);
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 06d84302a3..8f675e8f64 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -343,6 +343,7 @@ void register_scene_types() {
ClassDB::register_class<ColorPicker>();
ClassDB::register_class<ColorPickerButton>();
ClassDB::register_class<RichTextLabel>();
+ ClassDB::register_class<RichTextEffect>();
ClassDB::register_class<PopupDialog>();
ClassDB::register_class<WindowDialog>();
ClassDB::register_class<AcceptDialog>();
diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp
index 7cd765fb5d..0c39c3cbb1 100644
--- a/scene/resources/mesh_data_tool.cpp
+++ b/scene/resources/mesh_data_tool.cpp
@@ -165,11 +165,12 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
e.vertex[0] = edge.x;
e.vertex[1] = edge.y;
edges.push_back(e);
+ v[j]->edges.push_back(face.edges[j]);
+ v[(j + 1) % 3]->edges.push_back(face.edges[j]);
}
edges.write[face.edges[j]].faces.push_back(fidx);
v[j]->faces.push_back(fidx);
- v[j]->edges.push_back(face.edges[j]);
}
faces.push_back(face);