diff options
156 files changed, 3373 insertions, 1809 deletions
diff --git a/.gitattributes b/.gitattributes index 03c6f96f80..47ba2b45bb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,4 @@ drivers/* linguist-vendored *.py eol=lf *.hpp eol=lf *.xml eol=lf +*.natvis eol=lf diff --git a/.gitignore b/.gitignore index 6db75f2324..52937b8679 100644 --- a/.gitignore +++ b/.gitignore @@ -91,6 +91,9 @@ bld/ *.debug *.dSYM +# Visual Studio cache/options directory +.vs/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -98,9 +101,6 @@ bld/ # Hints for improving IntelliSense, created together with VS project cpp.hint -# Visualizers for the VS debugger -*.natvis - #NUNIT *.VisualState.xml TestResult.xml diff --git a/.travis.yml b/.travis.yml index ea182027ad..7161a3e029 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ matrix: - clang-format-6.0 - libstdc++6 # >= 4.9 needed for clang-format-6.0 - - env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-mono-gcc EXTRA_ARGS="module_mono_enabled=yes mono_glue=no" + - env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc EXTRA_ARGS="module_mono_enabled=yes mono_glue=no" os: linux compiler: gcc addons: @@ -49,7 +49,7 @@ matrix: build_command: "scons p=x11 -j2 $OPTIONS" branch_pattern: coverity_scan - - env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang os: linux compiler: clang addons: @@ -57,19 +57,19 @@ matrix: packages: - *linux_deps - - env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang os: linux compiler: clang - - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-clang + - env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang os: osx compiler: clang - - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang + - env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang os: osx compiler: clang - - env: GODOT_TARGET=server TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-gcc + - env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc os: linux compiler: gcc addons: @@ -83,18 +83,18 @@ before_install: fi install: - - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$GODOT_TARGET" = "android" ]; then + - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$PLATFORM" = "android" ]; then misc/travis/android-tools-linux.sh; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then misc/travis/scons-local-osx.sh; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$GODOT_TARGET" = "android" ]; then + - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$PLATFORM" = "android" ]; then misc/travis/android-tools-osx.sh; fi before_script: - - if [ "$GODOT_TARGET" = "android" ]; then + - if [ "$PLATFORM" = "android" ]; then export ANDROID_HOME=$TRAVIS_BUILD_DIR/godot-dev/build-tools/android-sdk; export ANDROID_NDK_ROOT=$TRAVIS_BUILD_DIR/godot-dev/build-tools/android-ndk; fi @@ -103,5 +103,5 @@ script: - if [ "$STATIC_CHECKS" = "yes" ]; then sh ./misc/travis/clang-format.sh; else - scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS $EXTRA_ARGS $OPTIONS; + scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $EXTRA_ARGS $OPTIONS; fi diff --git a/SConstruct b/SConstruct index 3f3976555d..1c55e0be93 100644 --- a/SConstruct +++ b/SConstruct @@ -169,9 +169,11 @@ opts.Add(BoolVariable('progress', "Show a progress indicator during compilation" opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False)) opts.Add(EnumVariable('macports_clang', "Build using Clang from MacPorts", 'no', ('no', '5.0', 'devel'))) opts.Add(BoolVariable('no_editor_splash', "Don't use the custom splash screen for the editor", False)) +opts.Add('system_certs_path', "Use this path as SSL certificates default for editor (for package maintainers)", '') # Thirdparty libraries opts.Add(BoolVariable('builtin_bullet', "Use the built-in Bullet library", True)) +opts.Add(BoolVariable('builtin_certs', "Bundle default SSL certificates to be used if you don't specify an override in the project settings", True)) opts.Add(BoolVariable('builtin_enet', "Use the built-in ENet library", True)) opts.Add(BoolVariable('builtin_freetype', "Use the built-in FreeType library", True)) opts.Add(BoolVariable('builtin_libogg', "Use the built-in libogg library", True)) diff --git a/core/SCsub b/core/SCsub index a6365bf925..6746cc871a 100644 --- a/core/SCsub +++ b/core/SCsub @@ -93,6 +93,9 @@ if 'builtin_zstd' in env and env['builtin_zstd']: # Godot's own sources env.add_source_files(env.core_sources, "*.cpp") +# Certificates +env.Depends("#core/io/certs_compressed.gen.h", ["#thirdparty/certs/ca-certificates.crt", env.Value(env['builtin_certs']), env.Value(env['system_certs_path'])]) +env.CommandNoCache("#core/io/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", run_in_subprocess(core_builders.make_certs_header)) # Make binders env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) diff --git a/core/core_builders.py b/core/core_builders.py index 90e505aab9..f3a9e3b221 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -4,7 +4,40 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki """ from platform_methods import subprocess_main -from compat import iteritems, itervalues, open_utf8, escape_string +from compat import iteritems, itervalues, open_utf8, escape_string, byte_to_str + + +def make_certs_header(target, source, env): + + src = source[0] + dst = target[0] + f = open(src, "rb") + g = open_utf8(dst, "w") + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _CERTS_RAW_H\n") + g.write("#define _CERTS_RAW_H\n") + + # System certs path. Editor will use them if defined. (for package maintainers) + path = env['system_certs_path'] + g.write("#define _SYSTEM_CERTS_PATH \"%s\"\n" % str(path)) + if env['builtin_certs']: + # Defined here and not in env so changing it does not trigger a full rebuild. + g.write("#define BUILTIN_CERTS_ENABLED\n") + g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") + g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") + g.write("static const unsigned char _certs_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") + + g.close() + f.close() def make_authors_header(target, source, env): diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index de0b6860f9..ac563df0c3 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -375,6 +375,18 @@ Error HTTPClient::poll() { } } break; case STATUS_CONNECTED: { + // Check if we are still connected + if (ssl) { + Ref<StreamPeerSSL> tmp = connection; + tmp->poll(); + if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + } else if (tcp_connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } // Connection established, requests can now be made return OK; } break; @@ -668,11 +680,11 @@ Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received // We can't use StreamPeer.get_data, since when reaching EOF we will get an // error without knowing how many bytes we received. Error err = ERR_FILE_EOF; - int read; + int read = 0; int left = p_bytes; r_received = 0; while (left > 0) { - err = connection->get_partial_data(p_buffer, left, read); + err = connection->get_partial_data(p_buffer + r_received, left, read); if (err == OK) { r_received += read; } else if (err == ERR_FILE_EOF) { diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 156a842e35..3f608b720c 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -209,6 +209,12 @@ void StreamPeer::put_double(double p_val) { } put_data(buf, 8); } +void StreamPeer::put_string(const String &p_string) { + + CharString cs = p_string.ascii(); + put_u32(cs.length()); + put_data((const uint8_t *)cs.get_data(), cs.length()); +} void StreamPeer::put_utf8_string(const String &p_string) { CharString cs = p_string.utf8(); @@ -325,6 +331,8 @@ double StreamPeer::get_double() { } String StreamPeer::get_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<char> buf; @@ -337,6 +345,8 @@ String StreamPeer::get_string(int p_bytes) { } String StreamPeer::get_utf8_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<uint8_t> buf; @@ -386,6 +396,7 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("put_u64", "value"), &StreamPeer::put_u64); ClassDB::bind_method(D_METHOD("put_float", "value"), &StreamPeer::put_float); ClassDB::bind_method(D_METHOD("put_double", "value"), &StreamPeer::put_double); + ClassDB::bind_method(D_METHOD("put_string", "value"), &StreamPeer::put_string); ClassDB::bind_method(D_METHOD("put_utf8_string", "value"), &StreamPeer::put_utf8_string); ClassDB::bind_method(D_METHOD("put_var", "value"), &StreamPeer::put_var); @@ -399,8 +410,8 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_u64"), &StreamPeer::get_u64); ClassDB::bind_method(D_METHOD("get_float"), &StreamPeer::get_float); ClassDB::bind_method(D_METHOD("get_double"), &StreamPeer::get_double); - ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string); - ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string); + ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_var"), &StreamPeer::get_var); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian_enabled"); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 9d2e0340b0..f189960cbd 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -71,6 +71,7 @@ public: void put_u64(uint64_t p_val); void put_float(float p_val); void put_double(double p_val); + void put_string(const String &p_string); void put_utf8_string(const String &p_string); void put_var(const Variant &p_variant); @@ -84,8 +85,8 @@ public: int64_t get_64(); float get_float(); double get_double(); - String get_string(int p_bytes); - String get_utf8_string(int p_bytes); + String get_string(int p_bytes = -1); + String get_utf8_string(int p_bytes = -1); Variant get_var(); StreamPeer() { big_endian = false; } diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 1f59021938..138f91301e 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -30,6 +30,8 @@ #include "stream_peer_ssl.h" +#include "core/io/certs_compressed.gen.h" +#include "core/io/compression.h" #include "core/os/file_access.h" #include "core/project_settings.h" @@ -42,13 +44,20 @@ StreamPeerSSL *StreamPeerSSL::create() { StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL; bool StreamPeerSSL::available = false; -bool StreamPeerSSL::initialize_certs = true; void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) { if (load_certs_func) load_certs_func(p_memory); } +void StreamPeerSSL::load_certs_from_file(String p_path) { + if (p_path != "") { + PoolByteArray certs = get_cert_file_as_array(p_path); + if (certs.size() > 0) + load_certs_func(certs); + } +} + bool StreamPeerSSL::is_available() { return available; } @@ -61,6 +70,25 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { return blocking_handshake; } +PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) { + + PoolByteArray out; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (f) { + int flen = f->get_len(); + out.resize(flen + 1); + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; // Make sure it ends with string terminator + memdelete(f); +#ifdef DEBUG_ENABLED + print_verbose(vformat("Loaded certs from '%s'.", p_path)); +#endif + } + + return out; +} + PoolByteArray StreamPeerSSL::get_project_cert_array() { PoolByteArray out; @@ -68,24 +96,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); if (certs_path != "") { - - FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); - if (f) { - int flen = f->get_len(); - out.resize(flen + 1); - { - PoolByteArray::Write w = out.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; //end f string - } - - memdelete(f); - + // Use certs defined in project settings. + return get_cert_file_as_array(certs_path); + } +#ifdef BUILTIN_CERTS_ENABLED + else { + // Use builtin certs only if user did not override it in project settings. + out.resize(_certs_uncompressed_size + 1); + PoolByteArray::Write w = out.write(); + Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); + w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator #ifdef DEBUG_ENABLED - print_verbose(vformat("Loaded certs from '%s'.", certs_path)); + print_verbose("Loaded builtin certs"); #endif - } } +#endif return out; } @@ -103,6 +128,7 @@ void StreamPeerSSL::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); + BIND_ENUM_CONSTANT(STATUS_HANDSHAKING); BIND_ENUM_CONSTANT(STATUS_CONNECTED); BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index f66c1c7de9..8ce36d7e7d 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -46,9 +46,6 @@ protected: static LoadCertsFromMemory load_certs_func; static bool available; - friend class Main; - static bool initialize_certs; - bool blocking_handshake; public: @@ -72,7 +69,9 @@ public: static StreamPeerSSL *create(); + static PoolByteArray get_cert_file_as_array(String p_path); static PoolByteArray get_project_cert_array(); + static void load_certs_from_file(String p_path); static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index f4bf8a13ae..28561e8cbc 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -249,6 +249,23 @@ StreamPeerTCP::Status StreamPeerTCP::get_status() { if (status == STATUS_CONNECTING) { _poll_connection(); + } else if (status == STATUS_CONNECTED) { + Error err; + err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); + if (err == OK) { + // FIN received + if (_sock->get_available_bytes() == 0) { + disconnect_from_host(); + return status; + } + } + // Also poll write + err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0); + if (err != OK && err != ERR_BUSY) { + // Got an error + disconnect_from_host(); + status = STATUS_ERROR; + } } return status; diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 5c8512d8bd..0c06d2a2b5 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -57,7 +57,7 @@ uint32_t Math::rand() { } int Math::step_decimals(double p_step) { - static const int maxn = 9; + static const int maxn = 10; static const double sd[maxn] = { 0.9999, // somehow compensate for floating point error 0.09999, @@ -67,17 +67,19 @@ int Math::step_decimals(double p_step) { 0.000009999, 0.0000009999, 0.00000009999, - 0.000000009999 + 0.000000009999, + 0.0000000009999 }; - double as = Math::abs(p_step); + double abs = Math::abs(p_step); + double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < maxn; i++) { - if (as >= sd[i]) { + if (decs >= sd[i]) { return i; } } - return maxn; + return 0; } double Math::dectime(double p_value, double p_amount, double p_step) { diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 704b4d1fdc..fe1dc2cdd1 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -662,11 +662,14 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { prop.push_back(Variant()); } else { prop.push_back(pi.hint); - if (res.is_null() || res->get_path().empty()) - prop.push_back(pi.hint_string); - else - prop.push_back(String("RES:") + res->get_path()); + prop.push_back(pi.hint_string); prop.push_back(pi.usage); + if (!res.is_null()) { + var = String("PATH") + res->get_path(); + } else if (var.get_type() == Variant::STRING) { + var = String("DATA") + var; + } + prop.push_back(var); } send_props.push_back(prop); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 2edf33ec1c..9afc31a772 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -1656,13 +1656,13 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { } else if (p_index == CoreStringNames::singleton->a) { return v->a; } else if (p_index == CoreStringNames::singleton->r8) { - return int(v->r * 255.0); + return int(Math::round(v->r * 255.0)); } else if (p_index == CoreStringNames::singleton->g8) { - return int(v->g * 255.0); + return int(Math::round(v->g * 255.0)); } else if (p_index == CoreStringNames::singleton->b8) { - return int(v->b * 255.0); + return int(Math::round(v->b * 255.0)); } else if (p_index == CoreStringNames::singleton->a8) { - return int(v->a * 255.0); + return int(Math::round(v->a * 255.0)); } else if (p_index == CoreStringNames::singleton->h) { return v->get_h(); } else if (p_index == CoreStringNames::singleton->s) { diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index 3e46dc4e92..7bd332a3e4 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -120,9 +120,9 @@ <method name="atan2"> <return type="float"> </return> - <argument index="0" name="x" type="float"> + <argument index="0" name="y" type="float"> </argument> - <argument index="1" name="y" type="float"> + <argument index="1" name="x" type="float"> </argument> <description> Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant. diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 98404478f4..60f097f3f9 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -576,7 +576,7 @@ Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied. </constant> <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29"> - Canvas item transform has changed. Only received if requested. + Canvas item transform has changed. Notification is only received if enabled by [method set_notify_transform] or [method set_notify_local_transform]. </constant> <constant name="NOTIFICATION_DRAW" value="30"> CanvasItem is requested to draw. diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index ec0381bda5..a4709c1c86 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -18,7 +18,7 @@ var err = config.load("user://settings.cfg") if err == OK: # if not, something went wrong with the file loading # Look for the display/width pair, and default to 1024 if missing - var screen_width = get_value("display", "width", 1024) + var screen_width = config.get_value("display", "width", 1024) # Store a variable if and only if it hasn't been defined yet if not config.has_section_key("audio", "mute"): config.set_value("audio", "mute", false) diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml index ade6d2034d..5a8b506f9e 100644 --- a/doc/classes/EditorFileSystem.xml +++ b/doc/classes/EditorFileSystem.xml @@ -93,10 +93,6 @@ Remitted if a resource is reimported. </description> </signal> - <signal name="script_classes_updated"> - <description> - </description> - </signal> <signal name="sources_changed"> <argument index="0" name="exist" type="bool"> </argument> diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index b21d402468..fdc204605e 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -41,6 +41,7 @@ return FAILED var mesh = Mesh.new() + # Fill the Mesh with data read in 'file', left as exercise to the reader var filename = save_path + "." + get_save_extension() ResourceSaver.save(filename, mesh) diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 19bd7e6d52..f073c5e40b 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="EditorInterface" inherits="Node" category="Core" version="3.1"> <brief_description> - Editor interface and main components. + Godot editor's interface. </brief_description> <description> - Editor interface. Allows saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, as well as information about scenes. Also see [EditorPlugin] and [EditorScript]. + EditorInterface gives you control over Godot editor's window. It allows customizing the window, saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects, and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, and information about scenes. </description> <tutorials> </tutorials> @@ -24,14 +24,14 @@ <return type="Control"> </return> <description> - Returns the base [Control]. + Returns the main container of Godot editor's window. You can use it, for example, to retrieve the size of the container and place your controls accordingly. </description> </method> <method name="get_edited_scene_root"> <return type="Node"> </return> <description> - Returns the edited scene's root [Node]. + Returns the edited (current) scene's root [Node]. </description> </method> <method name="get_editor_settings"> @@ -52,7 +52,7 @@ <return type="Array"> </return> <description> - Returns an [Array] of the currently opened scenes. + Returns an [Array] with the file paths of the currently opened scenes. </description> </method> <method name="get_resource_filesystem"> @@ -66,7 +66,7 @@ <return type="EditorResourcePreview"> </return> <description> - Returns the [EditorResourcePreview]\ er. + Returns the [EditorResourcePreview]. </description> </method> <method name="get_script_editor"> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 208780547e..ac139f18c9 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -4,7 +4,7 @@ Used by the editor to extend its functionality. </brief_description> <description> - Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. + Plugins are used by the editor to extend functionality. The most common types of plugins are those which edit a given node or resource type, import plugins and export plugins. Also see [EditorScript] to add functions to the editor. </description> <tutorials> <link>http://docs.godotengine.org/en/3.0/development/plugins/index.html</link> @@ -31,7 +31,7 @@ <argument index="1" name="title" type="String"> </argument> <description> - Add a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_bottom_panel]. + Add a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_bottom_panel] and free it with [code]queue_free()[/code]. </description> </method> <method name="add_control_to_container"> @@ -44,7 +44,7 @@ <description> Add a custom control to a container (see CONTAINER_* enum). There are many locations where custom controls can be added in the editor UI. Please remember that you have to manage the visibility of your custom controls yourself (and likely hide it after adding it). - If your plugin is being removed, also make sure to remove your custom controls too. + When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_container] and free it with [code]queue_free()[/code]. </description> </method> <method name="add_control_to_dock"> @@ -57,7 +57,7 @@ <description> Add the control to a specific dock slot (see DOCK_* enum for options). If the dock is repositioned and as long as the plugin is active, the editor will save the dock position on further sessions. - If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_docks]. + When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_docks] and free it with [code]queue_free()[/code]. </description> </method> <method name="add_custom_type"> @@ -74,7 +74,7 @@ <description> Add a custom type, which will appear in the list of nodes or resources. An icon can be optionally passed. When given node or resource is selected, the base type will be instanced (ie, "Spatial", "Control", "Resource"), then the script will be loaded and set to this object. - You can use the [method EditorPlugin.handles] to check if your custom object is being edited by checking the script or using 'is' keyword. + You can use the virtual method [method handles] to check if your custom object is being edited by checking the script or using 'is' keyword. During run-time, this will be a simple object with a script so this function does not need to be called then. </description> </method> @@ -122,7 +122,7 @@ <argument index="3" name="ud" type="Variant" default="null"> </argument> <description> - Adds a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it. + Add a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it. </description> </method> <method name="add_tool_submenu_item"> @@ -133,6 +133,7 @@ <argument index="1" name="submenu" type="Object"> </argument> <description> + Like [method add_tool_menu_item] but adds the [code]submenu[/code] item inside the [code]name[/code] menu. </description> </method> <method name="apply_changes" qualifiers="virtual"> @@ -165,15 +166,21 @@ This function is used for plugins that edit specific object types (nodes or resources). It requests the editor to edit the given object. </description> </method> - <method name="forward_canvas_gui_input" qualifiers="virtual"> - <return type="bool"> + <method name="forward_canvas_draw_over_viewport" qualifiers="virtual"> + <return type="void"> </return> - <argument index="0" name="event" type="InputEvent"> + <argument index="0" name="overlay" type="Control"> </argument> <description> + This method is called when there is an input event in the 2D viewport, e.g. the user clicks with the mouse in the 2D space (canvas GUI). Keep in mind that for this method to be called you have to first declare the virtual method [method handles] so the editor knows that you want to work with the workspace: + [codeblock] + func handles(object): + return true + [/codeblock] + Also note that the edited scene must have a root node. </description> </method> - <method name="forward_draw_over_viewport" qualifiers="virtual"> + <method name="forward_canvas_force_draw_over_viewport" qualifiers="virtual"> <return type="void"> </return> <argument index="0" name="overlay" type="Control"> @@ -181,10 +188,10 @@ <description> </description> </method> - <method name="forward_force_draw_over_viewport" qualifiers="virtual"> - <return type="void"> + <method name="forward_canvas_gui_input" qualifiers="virtual"> + <return type="bool"> </return> - <argument index="0" name="overlay" type="Control"> + <argument index="0" name="event" type="InputEvent"> </argument> <description> </description> @@ -197,8 +204,12 @@ <argument index="1" name="event" type="InputEvent"> </argument> <description> - Implement this function if you are interested in 3D view screen input events. It will be called only if currently selected node is handled by your plugin. - If you would like to always gets those input events then additionally use [method set_input_forwarding_always_enabled]. + This method is called when there is an input event in the 3D viewport, e.g. the user clicks with the mouse in the 3D space (spatial GUI). Keep in mind that for this method to be called you have to first declare the virtual method [method handles] so the editor knows that you want to work with the workspace: + [codeblock] + func handles(object): + return true + [/codeblock] + Also note that the edited scene must have a root node. </description> </method> <method name="get_breakpoints" qualifiers="virtual"> @@ -212,6 +223,7 @@ <return type="EditorInterface"> </return> <description> + Return the [EditorInterface] object that gives you control over Godot editor's window and its functionalities. </description> </method> <method name="get_plugin_icon" qualifiers="virtual"> @@ -253,7 +265,7 @@ <argument index="0" name="layout" type="ConfigFile"> </argument> <description> - Get the GUI layout of the plugin. This is used to save the project's editor layout when the [method EditorPlugin.queue_save_layout] is called or the editor layout was changed(For example changing the position of a dock). + Get the GUI layout of the plugin. This is used to save the project's editor layout when [method queue_save_layout] is called or the editor layout was changed(For example changing the position of a dock). </description> </method> <method name="handles" qualifiers="virtual"> @@ -262,14 +274,14 @@ <argument index="0" name="object" type="Object"> </argument> <description> - Implement this function if your plugin edits a specific type of object (Resource or Node). If you return true, then you will get the functions [method EditorPlugin.edit] and [method EditorPlugin.make_visible] called when the editor requests them. + Implement this function if your plugin edits a specific type of object (Resource or Node). If you return true, then you will get the functions [method EditorPlugin.edit] and [method EditorPlugin.make_visible] called when the editor requests them. If you have declared the methods [method forward_canvas_gui_input] and [method forward_spatial_gui_input] these will be called too. </description> </method> <method name="has_main_screen" qualifiers="virtual"> <return type="bool"> </return> <description> - Return true if this is a main screen editor plugin (it goes in the main screen selector together with 2D, 3D, Script). + Return true if this is a main screen editor plugin (it goes in the workspaces selector together with '2D', '3D', and 'Script'). </description> </method> <method name="hide_bottom_panel"> @@ -318,7 +330,7 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Remove the control from the bottom panel. Don't forget to call this if you added one, so the editor can remove it cleanly. + Remove the control from the bottom panel. You have to manually [code]queue_free()[/code] the control. </description> </method> <method name="remove_control_from_container"> @@ -329,7 +341,7 @@ <argument index="1" name="control" type="Control"> </argument> <description> - Remove the control from the specified container. Use it when cleaning up after adding a control with [method add_control_to_container]. Note that you can simply free the control if you won't use it anymore. + Remove the control from the specified container. You have to manually [code]queue_free()[/code] the control. </description> </method> <method name="remove_control_from_docks"> @@ -338,7 +350,7 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Remove the control from the dock. Don't forget to call this if you added one, so the editor can save the layout and remove it cleanly. + Remove the control from the dock. You have to manually [code]queue_free()[/code] the control. </description> </method> <method name="remove_custom_type"> @@ -347,7 +359,7 @@ <argument index="0" name="type" type="String"> </argument> <description> - Remove a custom type added by [method EditorPlugin.add_custom_type] + Remove a custom type added by [method add_custom_type] </description> </method> <method name="remove_export_plugin"> @@ -441,7 +453,7 @@ <argument index="0" name="screen_name" type="String"> </argument> <description> - Emitted when user change main screen view (2D, 3D, Script, AssetLib). Works also with screens which are defined by plugins. + Emitted when user change the workspace (2D, 3D, Script, AssetLib). Also works with custom screens defined by plugins. </description> </signal> <signal name="resource_saved"> diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml index fb9af47b1f..c4dcbbbc82 100644 --- a/doc/classes/EditorResourcePreviewGenerator.xml +++ b/doc/classes/EditorResourcePreviewGenerator.xml @@ -16,10 +16,12 @@ </return> <argument index="0" name="from" type="Resource"> </argument> + <argument index="1" name="size" type="Vector2"> + </argument> <description> - Generate a preview from a given resource. This must be always implemented. - Returning an empty texture is an OK way to fail and let another generator take care. - Care must be taken because this function is always called from a thread (not the main thread). + Generate a preview from a given resource with the specified size. This must always be implemented. + Returning an empty texture is an OK way to fail and let another generator take care. + Care must be taken because this function is always called from a thread (not the main thread). </description> </method> <method name="generate_from_path" qualifiers="virtual"> @@ -27,10 +29,12 @@ </return> <argument index="0" name="path" type="String"> </argument> + <argument index="1" name="size" type="Vector2"> + </argument> <description> - Generate a preview directly from a path, implementing this is optional, as default code will load and call generate() - Returning an empty texture is an OK way to fail and let another generator take care. - Care must be taken because this function is always called from a thread (not the main thread). + Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method generate]. + Returning an empty texture is an OK way to fail and let another generator take care. + Care must be taken because this function is always called from a thread (not the main thread). </description> </method> <method name="handles" qualifiers="virtual"> diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml index 09cae25403..76c105dd25 100644 --- a/doc/classes/EditorScenePostImport.xml +++ b/doc/classes/EditorScenePostImport.xml @@ -4,44 +4,45 @@ Post process scenes after import </brief_description> <description> - The imported scene can be automatically modified right after import by specifying a 'custom script' that inherits from this class. The [method post_import]-method receives the imported scene's root-node and returns the modified version of the scene - </description> - <tutorials> - <link>http://docs.godotengine.org/en/latest/learning/workflow/assets/importing_scenes.html?highlight=post%20import</link> - </tutorials> - <demos> + Imported scenes can be automatically modified right after import by setting their [i]Custom Script[/i] Import property to a [code]tool[/code] script that inherits from this class. + The [method post_import] callback receives the imported scene's root node and returns the modified version of the scene. Usage example: [codeblock] -tool # needed so it runs in editor -extends EditorScenePostImport + tool # needed so it runs in editor + extends EditorScenePostImport -# This sample changes all node names + # This sample changes all node names -# get called right after the scene is imported and gets the root-node -func post_import(scene): - # change all node names to "modified_[oldnodename]" - iterate(scene) - return scene # remember to return the imported scene + # Called right after the scene is imported and gets the root node + func post_import(scene): + # change all node names to "modified_[oldnodename]" + iterate(scene) + return scene # remember to return the imported scene -func iterate(node): - if node!=null: - node.name = "modified_"+node.name - for child in node.get_children(): - iterate(child) -[/codeblock] + func iterate(node): + if node != null: + node.name = "modified_"+node.name + for child in node.get_children(): + iterate(child) + [/codeblock] + </description> + <tutorials> + <link>http://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link> + </tutorials> + <demos> </demos> <methods> <method name="get_source_file" qualifiers="const"> <return type="String"> </return> <description> - Returns the source-file-path which got imported (e.g. [code]res://scene.dae[/code] ) + Returns the source file path which got imported (e.g. [code]res://scene.dae[/code]). </description> </method> <method name="get_source_folder" qualifiers="const"> <return type="String"> </return> <description> - Returns the resource-folder the imported scene-file is located in + Returns the resource folder the imported scene file is located in. </description> </method> <method name="post_import" qualifiers="virtual"> @@ -50,7 +51,7 @@ func iterate(node): <argument index="0" name="scene" type="Object"> </argument> <description> - Gets called after the scene got imported and has to return the modified version of the scene + Gets called after the scene got imported and has to return the modified version of the scene. </description> </method> </methods> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index bd85075b7e..9d48669a6b 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -48,11 +48,11 @@ Erase a given setting (pass full property path). </description> </method> - <method name="get_favorite_dirs" qualifiers="const"> + <method name="get_favorites" qualifiers="const"> <return type="PoolStringArray"> </return> <description> - Get the list of favorite directories for this project. + Get the list of favorite files and directories for this project. </description> </method> <method name="get_project_metadata" qualifiers="const"> @@ -122,13 +122,13 @@ <description> </description> </method> - <method name="set_favorite_dirs"> + <method name="set_favorites"> <return type="void"> </return> <argument index="0" name="dirs" type="PoolStringArray"> </argument> <description> - Set the list of favorite directories for this project. + Set the list of favorite files and directories for this project. </description> </method> <method name="set_initial_value"> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 55693bd49c..56accdcd9e 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -487,10 +487,10 @@ OpenGL texture format RG with two components and a bitdepth of 8 for each. </constant> <constant name="FORMAT_RGB8" value="4" enum="Format"> - OpenGL texture format RGB with three components, each with a bitdepth of 8. + OpenGL texture format RGB with three components, each with a bitdepth of 8. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_RGBA8" value="5" enum="Format"> - OpenGL texture format RGBA with four components, each with a bitdepth of 8. + OpenGL texture format RGBA with four components, each with a bitdepth of 8. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_RGBA4444" value="6" enum="Format"> OpenGL texture format RGBA with four components, each with a bitdepth of 4. @@ -526,13 +526,13 @@ A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent. </constant> <constant name="FORMAT_DXT1" value="17" enum="Format"> - The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression. + The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_DXT3" value="18" enum="Format"> - The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas. + The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_DXT5" value="19" enum="Format"> - The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3. + The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_RGTC_R" value="20" enum="Format"> Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression. @@ -541,7 +541,7 @@ Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel. </constant> <constant name="FORMAT_BPTC_RGBA" value="22" enum="Format"> - Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression. + Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_BPTC_RGBF" value="23" enum="Format"> Texture format that uses BPTC compression with signed floating-point RGB components. @@ -550,7 +550,7 @@ Texture format that uses BPTC compression with unsigned floating-point RGB components. </constant> <constant name="FORMAT_PVRTC2" value="25" enum="Format"> - Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC. + Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_PVRTC2A" value="26" enum="Format"> Same as PVRTC2, but with an alpha component. @@ -577,13 +577,13 @@ Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data. </constant> <constant name="FORMAT_ETC2_RGB8" value="34" enum="Format"> - Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data. + Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format"> - Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support. + Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format"> - Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque. + Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque. Note that when creating an [ImageTexture], an sRGB to linear color space conversion is performed. </constant> <constant name="FORMAT_MAX" value="37" enum="Format"> </constant> diff --git a/doc/classes/ImageTexture.xml b/doc/classes/ImageTexture.xml index 0bff3317db..5c57899468 100644 --- a/doc/classes/ImageTexture.xml +++ b/doc/classes/ImageTexture.xml @@ -36,7 +36,7 @@ <argument index="1" name="flags" type="int" default="7"> </argument> <description> - Create a new [code]ImageTexture[/code] from an [Image] with "flags" from [Texture].FLAG_*. + Create a new [code]ImageTexture[/code] from an [Image] with "flags" from [Texture].FLAG_*. An sRGB to linear color space conversion can take place, according to [Image].FORMAT_*. </description> </method> <method name="get_format" qualifiers="const"> diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml index 14053c6a35..af00027ed3 100644 --- a/doc/classes/PhysicsBody.xml +++ b/doc/classes/PhysicsBody.xml @@ -27,6 +27,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -35,6 +36,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="remove_collision_exception_with"> @@ -54,6 +56,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -64,6 +67,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> </methods> @@ -74,7 +78,7 @@ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> - The physics layers this area can scan for collisions. + The physics layers this area scans for collisions. </member> </members> <constants> diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index ccc704c7ec..4278979049 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -27,7 +27,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Return an individual bit on the collision mask. + Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -36,7 +36,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Return an individual bit on the collision mask. + Returns an individual bit on the collision mask. </description> </method> <method name="remove_collision_exception_with"> @@ -56,7 +56,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Set/clear individual bits on the layer mask. This makes getting a body in/out of only one layer easier. + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -67,7 +67,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Set/clear individual bits on the collision mask. This makes selecting the areas scanned easier. + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> </methods> @@ -78,10 +78,10 @@ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> - The physics layers this area can scan for collisions. + The physics layers this area scans for collisions. </member> <member name="layers" type="int" setter="_set_layers" getter="_get_layers"> - Both collision_layer and collision_mask. Returns collision_layer when accessed. Updates collision_layers and collision_mask when modified. + Both [member collision_layer] and [member collision_mask]. Returns [member collision_layer] when accessed. Updates [member collision_layer] and [member collision_mask] when modified. </member> </members> <constants> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 548d60fe35..c05d6bc849 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -609,9 +609,6 @@ <member name="network/remote_fs/page_size" type="int" setter="" getter=""> Page size used by remote filesystem. </member> - <member name="network/ssl/certificates" type="String" setter="" getter=""> - If your game or application uses HTTPS, a certificates file is needed. It must be set here. - </member> <member name="node/name_casing" type="int" setter="" getter=""> When creating nodes names automatically, set the type of casing in this project. This is mostly an editor setting. </member> diff --git a/doc/classes/SceneState.xml b/doc/classes/SceneState.xml index 36cddf08df..bd2f883cae 100644 --- a/doc/classes/SceneState.xml +++ b/doc/classes/SceneState.xml @@ -4,7 +4,7 @@ A script interface to a scene file's data. </brief_description> <description> - Maintains a list of resources, nodes, exported and overridden properties, and built-in scripts associated with a scene. + Maintains a list of resources, nodes, exported, and overridden properties, and built-in scripts associated with a scene. </description> <tutorials> </tutorials> diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml index 6c13496fc4..2772538cec 100644 --- a/doc/classes/Shape2D.xml +++ b/doc/classes/Shape2D.xml @@ -22,7 +22,7 @@ <argument index="2" name="shape_xform" type="Transform2D"> </argument> <description> - Return whether this shape is colliding with another. + Returns [code]true[/code] if this shape is colliding with another. This method needs the transformation matrix for this shape ([code]local_xform[/code]), the shape to check collisions with ([code]with_shape[/code]), and the transformation matrix of that shape ([code]shape_xform[/code]). </description> </method> @@ -36,7 +36,7 @@ <argument index="2" name="shape_xform" type="Transform2D"> </argument> <description> - Return a list of the points where this shape touches another. If there are no collisions, the list is empty. + Returns a list of the points where this shape touches another. If there are no collisions the list is empty. This method needs the transformation matrix for this shape ([code]local_xform[/code]), the shape to check collisions with ([code]with_shape[/code]), and the transformation matrix of that shape ([code]shape_xform[/code]). </description> </method> @@ -72,7 +72,7 @@ <argument index="4" name="shape_motion" type="Vector2"> </argument> <description> - Return a list of the points where this shape would touch another, if a given movement was applied. If there are no collisions, the list is empty. + Returns a list of the points where this shape would touch another, if a given movement was applied. If there are no collisions the list is empty. This method needs the transformation matrix for this shape ([code]local_xform[/code]), the movement to test on this shape ([code]local_motion[/code]), the shape to check collisions with ([code]with_shape[/code]), the transformation matrix of that shape ([code]shape_xform[/code]), and the movement to test onto the other object ([code]shape_motion[/code]). </description> </method> diff --git a/doc/classes/ShortCut.xml b/doc/classes/ShortCut.xml index 6da9d7c59d..1b5fc035c2 100644 --- a/doc/classes/ShortCut.xml +++ b/doc/classes/ShortCut.xml @@ -16,7 +16,7 @@ <return type="String"> </return> <description> - Returns the Shortcut's [InputEvent] as a [String]. + Returns the shortcut's [InputEvent] as a [String]. </description> </method> <method name="is_shortcut" qualifiers="const"> @@ -25,20 +25,20 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> - Returns [code]true[/code] if the Shortcut's [InputEvent] equals [code]event[/code]. + Returns [code]true[/code] if the shortcut's [InputEvent] equals [code]event[/code]. </description> </method> <method name="is_valid" qualifiers="const"> <return type="bool"> </return> <description> - If [code]true[/code] this Shortcut is valid. + If [code]true[/code] this shortcut is valid. </description> </method> </methods> <members> <member name="shortcut" type="InputEvent" setter="set_shortcut" getter="get_shortcut"> - The Shortcut's [InputEvent]. + The shortcut's [InputEvent]. Generally the [InputEvent] is a keyboard key, though it can be any [InputEvent]. </member> </members> diff --git a/doc/classes/SoftBody.xml b/doc/classes/SoftBody.xml index c3c789a6de..196d29fc60 100644 --- a/doc/classes/SoftBody.xml +++ b/doc/classes/SoftBody.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="SoftBody" inherits="MeshInstance" category="Core" version="3.1"> <brief_description> + A soft mesh physics body. </brief_description> <description> + A deformable physics body. Used to create elastic or deformable objects such as cloth, rubber, or other flexible materials. </description> <tutorials> </tutorials> @@ -15,6 +17,7 @@ <argument index="0" name="body" type="Node"> </argument> <description> + Adds a body to the list of bodies that this body can't collide with. </description> </method> <method name="get_collision_layer_bit" qualifiers="const"> @@ -23,6 +26,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -31,6 +35,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> + Returns an individual bit on the collision mask. </description> </method> <method name="is_ray_pickable" qualifiers="const"> @@ -45,6 +50,7 @@ <argument index="0" name="body" type="Node"> </argument> <description> + Removes a body from the list of bodies that this body can't collide with. </description> </method> <method name="set_collision_layer_bit"> @@ -55,6 +61,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -65,6 +72,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> + Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_ray_pickable"> @@ -80,8 +88,12 @@ <member name="areaAngular_stiffness" type="float" setter="set_areaAngular_stiffness" getter="get_areaAngular_stiffness"> </member> <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer"> + The physics layers this area is in. + Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. + A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> + The physics layers this area scans for collisions. </member> <member name="damping_coefficient" type="float" setter="set_damping_coefficient" getter="get_damping_coefficient"> </member> @@ -96,6 +108,7 @@ <member name="pressure_coefficient" type="float" setter="set_pressure_coefficient" getter="get_pressure_coefficient"> </member> <member name="simulation_precision" type="int" setter="set_simulation_precision" getter="get_simulation_precision"> + Increasing this value will improve the resulting simulation, but can affect performance. Use with care. </member> <member name="total_mass" type="float" setter="set_total_mass" getter="get_total_mass"> </member> diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index ebe29c7e24..74ac8a79c0 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -81,10 +81,10 @@ <method name="get_string"> <return type="String"> </return> - <argument index="0" name="bytes" type="int"> + <argument index="0" name="bytes" type="int" default="-1"> </argument> <description> - Get a string with byte-length "bytes" from the stream. + Get a string with byte-length [code]bytes[/code] from the stream. If [code]bytes[/code] is negative (default) the length will be read from the stream using the reverse process of [method put_string]. </description> </method> <method name="get_u16"> @@ -118,10 +118,10 @@ <method name="get_utf8_string"> <return type="String"> </return> - <argument index="0" name="bytes" type="int"> + <argument index="0" name="bytes" type="int" default="-1"> </argument> <description> - Get a utf8 string with byte-length "bytes" from the stream (this decodes the string sent as utf8). + Get a utf8 string with byte-length [code]bytes[/code] from the stream (this decodes the string sent as utf8). If [code]bytes[/code] is negative (default) the length will be read from the stream using the reverse process of [method put_utf8_string]. </description> </method> <method name="get_var"> @@ -203,6 +203,15 @@ Send a chunk of data through the connection, if all the data could not be sent at once, only part of it will. This function returns two values, an Error code and an integer, describing how much data was actually sent. </description> </method> + <method name="put_string"> + <return type="void"> + </return> + <argument index="0" name="value" type="String"> + </argument> + <description> + Put a zero-terminated ascii string into the stream prepended by a 32 bits unsigned integer representing its size. + </description> + </method> <method name="put_u16"> <return type="void"> </return> @@ -245,7 +254,7 @@ <argument index="0" name="value" type="String"> </argument> <description> - Put a zero-terminated utf8 string into the stream. + Put a zero-terminated utf8 string into the stream prepended by a 32 bits unsigned integer representing its size. </description> </method> <method name="put_var"> diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index d1c8722901..65d638c4c0 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -32,25 +32,26 @@ <return type="void"> </return> <description> - Stop (cancel) the Timer. + Stops the timer. </description> </method> </methods> <members> <member name="autostart" type="bool" setter="set_autostart" getter="has_autostart"> - If [code]true[/code], Timer will automatically start when entering the scene tree. Default value: [code]false[/code]. + If [code]true[/code] the timer will automatically start when entering the scene tree. Default value: [code]false[/code]. </member> <member name="one_shot" type="bool" setter="set_one_shot" getter="is_one_shot"> - If [code]true[/code], Timer will stop when reaching 0. If [code]false[/code], it will restart. Default value: [code]false[/code]. + If [code]true[/code] the timer will stop when reaching 0. If [code]false[/code] it will restart. Default value: [code]false[/code]. </member> <member name="paused" type="bool" setter="set_paused" getter="is_paused"> - If [code]true[/code], the timer is paused and will not process until it is unpaused again, even if [method start] is called. + If [code]true[/code] the timer is paused and will not process until it is unpaused again, even if [method start] is called. </member> <member name="process_mode" type="int" setter="set_timer_process_mode" getter="get_timer_process_mode" enum="Timer.TimerProcessMode"> - Processing mode. Uses TIMER_PROCESS_* constants as value. + Processing mode. See [enum TimerProcessMode]. </member> <member name="time_left" type="float" setter="" getter="get_time_left"> The timer's remaining time in seconds. Returns 0 if the timer is inactive. + Note: You cannot set this value. To change the timer's remaining time, use [member wait_time]. </member> <member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time"> Wait time in seconds. @@ -59,16 +60,16 @@ <signals> <signal name="timeout"> <description> - Emitted when the Timer reaches 0. + Emitted when the timer reaches 0. </description> </signal> </signals> <constants> <constant name="TIMER_PROCESS_PHYSICS" value="0" enum="TimerProcessMode"> - Update the Timer during the physics step at each frame (fixed framerate processing). + Update the timer during the physics step at each frame (fixed framerate processing). </constant> <constant name="TIMER_PROCESS_IDLE" value="1" enum="TimerProcessMode"> - Update the Timer during the idle time at each frame. + Update the timer during the idle time at each frame. </constant> </constants> </class> diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index ad6601b18b..63a5c8cbbf 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -573,7 +573,7 @@ def make_rst_class(node): if events != None and len(list(events)) > 0: f.write(make_heading('Signals', '-')) for m in list(events): - f.write(" .. _class_" + name + "_" + m.attrib['name'] + ":\n\n") + f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n") make_method(f, name, m, True, True) f.write('\n') d = m.find('description') @@ -599,7 +599,7 @@ def make_rst_class(node): if len(enum_names) > 0: f.write(make_heading('Enumerations', '-')) for e in enum_names: - f.write(" .. _enum_" + name + "_" + e + ":\n\n") + f.write(".. _enum_" + name + "_" + e + ":\n\n") f.write("enum **" + e + "**:\n\n") for c in enums: if c.attrib['enum'] != e: @@ -624,6 +624,7 @@ def make_rst_class(node): if c.text.strip() != '': s += ' --- ' + rstize_text(c.text.strip(), name) f.write(s + '\n') + f.write('\n') # Class description descr = node.find('description') @@ -644,25 +645,25 @@ def make_rst_class(node): if match.lastindex == 2: # Doc reference with fragment identifier: emit direct link to section with reference to page, for example: # `#calling-javascript-from-script in Exporting For Web` - f.write("- `" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n") + f.write("- `" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n\n") # Commented out alternative: Instead just emit: # `Subsection in Exporting For Web` - # f.write("- `Subsection <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n") + # f.write("- `Subsection <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`\n\n") elif match.lastindex == 1: # Doc reference, for example: # `Math` - f.write("- :doc:`../" + groups[0] + "`\n") + f.write("- :doc:`../" + groups[0] + "`\n\n") else: # External link, for example: # `http://enet.bespin.org/usergroup0.html` - f.write("- `" + link + " <" + link + ">`_\n") + f.write("- `" + link + " <" + link + ">`_\n\n") # Property descriptions members = node.find('members') if members != None and len(list(members)) > 0: f.write(make_heading('Property Descriptions', '-')) for m in list(members): - f.write(" .. _class_" + name + "_" + m.attrib['name'] + ":\n\n") + f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n") make_properties(f, name, m, True) if m.text.strip() != '': f.write(rstize_text(m.text.strip(), name)) @@ -673,7 +674,7 @@ def make_rst_class(node): if methods != None and len(list(methods)) > 0: f.write(make_heading('Method Descriptions', '-')) for m in list(methods): - f.write(" .. _class_" + name + "_" + m.attrib['name'] + ":\n\n") + f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n") make_method(f, name, m, True) f.write('\n') d = m.find('description') diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 53f563303a..5203f53fa2 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -120,12 +120,12 @@ void main() { vec4 color = color_attrib; #ifdef USE_INSTANCING - mat4 extra_matrix2 = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0))); + mat4 extra_matrix_instance = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0))); color *= instance_color; vec4 instance_custom = instance_custom_data; #else - mat4 extra_matrix2 = extra_matrix; + mat4 extra_matrix_instance = extra_matrix; vec4 instance_custom = vec4(0.0); #endif @@ -157,7 +157,7 @@ void main() { #endif -#define extra_matrix extra_matrix2 +#define extra_matrix extra_matrix_instance { /* clang-format off */ @@ -246,8 +246,8 @@ VERTEX_SHADER_CODE pos = outvec.xy; #endif - local_rot.xy = normalize((modelview_matrix * (extra_matrix * vec4(1.0, 0.0, 0.0, 0.0))).xy); - local_rot.zw = normalize((modelview_matrix * (extra_matrix * vec4(0.0, 1.0, 0.0, 0.0))).xy); + local_rot.xy = normalize((modelview_matrix * (extra_matrix_instance * vec4(1.0, 0.0, 0.0, 0.0))).xy); + local_rot.zw = normalize((modelview_matrix * (extra_matrix_instance * vec4(0.0, 1.0, 0.0, 0.0))).xy); #ifdef USE_TEXTURE_RECT local_rot.xy *= sign(src_rect.z); local_rot.zw *= sign(src_rect.w); diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 9dcc6038ab..07548ab91f 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -68,7 +68,6 @@ #define SOCK_BUF(x) x #define SOCK_CBUF(x) x #define SOCK_IOCTL ioctl -#define SOCK_POLL ::poll #define SOCK_CLOSE ::close /* Windows */ @@ -80,7 +79,6 @@ #define SOCK_BUF(x) (char *)(x) #define SOCK_CBUF(x) (const char *)(x) #define SOCK_IOCTL ioctlsocket -#define SOCK_POLL WSAPoll #define SOCK_CLOSE closesocket // Windows doesn't have this flag @@ -331,10 +329,58 @@ Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) { return OK; } -Error NetSocketPosix::poll(PollType p_type, int timeout) const { +Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); +#if defined(WINDOWS_ENABLED) + bool ready = false; + fd_set rd, wr, ex; + fd_set *rdp = NULL; + fd_set *wrp = NULL; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(_sock, &ex); + struct timeval timeout = { p_timeout, 0 }; + // For blocking operation, pass NULL timeout pointer to select. + struct timeval *tp = NULL; + if (p_timeout >= 0) { + // If timeout is non-negative, we want to specify the timeout instead. + tp = &timeout; + } + + switch (p_type) { + case POLL_TYPE_IN: + FD_SET(_sock, &rd); + rdp = &rd; + break; + case POLL_TYPE_OUT: + FD_SET(_sock, &wr); + wrp = ≀ + break; + case POLL_TYPE_IN_OUT: + FD_SET(_sock, &rd); + FD_SET(_sock, &wr); + rdp = &rd; + wrp = ≀ + } + int ret = select(1, rdp, wrp, &ex, tp); + + ERR_FAIL_COND_V(ret == SOCKET_ERROR, FAILED); + + if (ret == 0) + return ERR_BUSY; + + ERR_FAIL_COND_V(FD_ISSET(_sock, &ex), FAILED); + + if (rdp && FD_ISSET(_sock, rdp)) + ready = true; + if (wrp && FD_ISSET(_sock, wrp)) + ready = true; + + return ready ? OK : ERR_BUSY; +#else struct pollfd pfd; pfd.fd = _sock; pfd.events = POLLIN; @@ -351,14 +397,16 @@ Error NetSocketPosix::poll(PollType p_type, int timeout) const { pfd.events = POLLOUT || POLLIN; } - int ret = SOCK_POLL(&pfd, 1, timeout); + int ret = ::poll(&pfd, 1, p_timeout); ERR_FAIL_COND_V(ret < 0, FAILED); + ERR_FAIL_COND_V(pfd.revents & POLLERR, FAILED); if (ret == 0) return ERR_BUSY; return OK; +#endif } Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { diff --git a/editor/SCsub b/editor/SCsub index 6a4b06a97a..82b982eef2 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -61,10 +61,6 @@ if env['tools']: env.Depends("#editor/doc_data_compressed.gen.h", docs) env.CommandNoCache("#editor/doc_data_compressed.gen.h", docs, run_in_subprocess(editor_builders.make_doc_header)) - # Certificates - env.Depends("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt") - env.CommandNoCache("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", run_in_subprocess(editor_builders.make_certs_header)) - import glob path = env.Dir('.').abspath diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 80bc73bc12..33d36e5e9c 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -374,7 +374,7 @@ void FindReplaceBar::_hide_bar() { void FindReplaceBar::_show_search() { show(); - search_text->grab_focus(); + search_text->call_deferred("grab_focus"); if (text_edit->is_selection_active() && !selection_only->is_pressed()) { search_text->set_text(text_edit->get_selection_text()); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 456e2fa1f0..a1337268ba 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -432,6 +432,9 @@ void ConnectionsDock::_make_or_edit_connection() { if (add_script_function) { // pick up args here before "it" is deleted by update_tree script_function_args = it->get_metadata(0).operator Dictionary()["args"]; + for (int i = 0; i < cToMake.binds.size(); i++) { + script_function_args.append("extra_arg_" + itos(i)); + } } if (connect_dialog->is_editing()) { diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index ff34618b04..eb11aea9cc 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -55,9 +55,9 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) { while (!f->eof_reached()) { String l = f->get_line().strip_edges(); + String name = l.split(" ")[0]; - if (l != String()) { - + if (ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) { TreeItem *ti = recent->create_item(root); ti->set_text(0, l); ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 037543f45c..d64b02a605 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -504,7 +504,7 @@ void DependencyRemoveDialog::ok_pressed() { } if (dirs_to_delete.size() == 0) { - //If we only deleted files we should only need to tell the file system about the files we touched. + // If we only deleted files we should only need to tell the file system about the files we touched. for (int i = 0; i < files_to_delete.size(); ++i) EditorFileSystem::get_singleton()->update_file(files_to_delete[i]); } else { @@ -518,21 +518,25 @@ void DependencyRemoveDialog::ok_pressed() { } } - // if some dirs would be deleted, favorite dirs need to be updated - Vector<String> previous_favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs(); - Vector<String> new_favorite_dirs; + EditorFileSystem::get_singleton()->scan_changes(); + } - for (int i = 0; i < previous_favorite_dirs.size(); ++i) { - if (dirs_to_delete.find(previous_favorite_dirs[i] + "/") < 0) { - new_favorite_dirs.push_back(previous_favorite_dirs[i]); - } - } + // If some files/dirs would be deleted, favorite dirs need to be updated + Vector<String> previous_favorites = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorites; - if (new_favorite_dirs.size() < previous_favorite_dirs.size()) { - EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs); + for (int i = 0; i < previous_favorites.size(); ++i) { + if (previous_favorites[i].ends_with("/")) { + if (dirs_to_delete.find(previous_favorites[i]) < 0) + new_favorites.push_back(previous_favorites[i]); + } else { + if (files_to_delete.find(previous_favorites[i]) < 0) + new_favorites.push_back(previous_favorites[i]); } + } - EditorFileSystem::get_singleton()->scan_changes(); + if (new_favorites.size() < previous_favorites.size()) { + EditorSettings::get_singleton()->set_favorites(new_favorites); } } diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index e4602f0f94..cdf0e4b829 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -113,7 +113,6 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St EditorAbout::EditorAbout() { set_title(TTR("Thanks from the Godot community!")); - get_ok()->set_text(TTR("OK")); set_hide_on_ok(true); set_resizable(true); diff --git a/editor/editor_builders.py b/editor/editor_builders.py index fa037980c2..9e9fe752b4 100644 --- a/editor/editor_builders.py +++ b/editor/editor_builders.py @@ -9,32 +9,6 @@ from platform_methods import subprocess_main from compat import encode_utf8, byte_to_str, open_utf8, escape_string -def make_certs_header(target, source, env): - - src = source[0] - dst = target[0] - f = open(src, "rb") - g = open_utf8(dst, "w") - buf = f.read() - decomp_size = len(buf) - import zlib - buf = zlib.compress(buf) - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _CERTS_RAW_H\n") - g.write("#define _CERTS_RAW_H\n") - g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") - g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") - g.write("static const unsigned char _certs_compressed[] = {\n") - for i in range(len(buf)): - g.write("\t" + byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") - - g.close() - f.close() - - def make_doc_header(target, source, env): dst = target[0] diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 3659a06bb7..08fd8a1319 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -198,8 +198,18 @@ void EditorFileDialog::update_dir() { dir->set_text(dir_access->get_current_dir()); - // Disable "Open" button only when we in selecting file(s) mode or open dir mode. + // Disable "Open" button only when selecting file(s) mode. get_ok()->set_disabled(_is_open_should_be_disabled()); + switch (mode) { + + case MODE_OPEN_FILE: + case MODE_OPEN_FILES: + get_ok()->set_text(TTR("Open")); + break; + case MODE_OPEN_DIR: + get_ok()->set_text(TTR("Select Current Folder")); + break; + } } void EditorFileDialog::_dir_entered(String p_dir) { @@ -269,7 +279,7 @@ void EditorFileDialog::_post_popup() { set_process_unhandled_input(true); } -void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { if (display_mode == DISPLAY_LIST || p_preview.is_null()) return; @@ -284,7 +294,7 @@ void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture } } -void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { set_process(false); preview_waiting = false; @@ -453,6 +463,8 @@ void EditorFileDialog::_item_selected(int p_item) { file->set_text(d["name"]); _request_single_thumbnail(get_current_dir().plus_file(get_current_file())); + } else if (mode == MODE_OPEN_DIR) { + get_ok()->set_text(TTR("Select This Folder")); } get_ok()->set_disabled(_is_open_should_be_disabled()); @@ -637,7 +649,7 @@ bool EditorFileDialog::_is_open_should_be_disabled() { Vector<int> items = item_list->get_selected_items(); if (items.size() == 0) - return true; + return mode != MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. for (int i = 0; i < items.size(); i++) { @@ -1115,7 +1127,7 @@ void EditorFileDialog::_update_drives() { void EditorFileDialog::_favorite_selected(int p_idx) { - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); ERR_FAIL_INDEX(p_idx, favorited.size()); dir_access->change_dir(favorited[p_idx]); @@ -1130,7 +1142,7 @@ void EditorFileDialog::_favorite_move_up() { int current = favorites->get_current(); if (current > 0 && current < favorites->get_item_count()) { - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); int a_idx = favorited.find(String(favorites->get_item_metadata(current - 1))); int b_idx = favorited.find(String(favorites->get_item_metadata(current))); @@ -1139,7 +1151,7 @@ void EditorFileDialog::_favorite_move_up() { return; SWAP(favorited.write[a_idx], favorited.write[b_idx]); - EditorSettings::get_singleton()->set_favorite_dirs(favorited); + EditorSettings::get_singleton()->set_favorites(favorited); _update_favorites(); update_file_list(); @@ -1150,7 +1162,7 @@ void EditorFileDialog::_favorite_move_down() { int current = favorites->get_current(); if (current >= 0 && current < favorites->get_item_count() - 1) { - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); int a_idx = favorited.find(String(favorites->get_item_metadata(current + 1))); int b_idx = favorited.find(String(favorites->get_item_metadata(current))); @@ -1159,7 +1171,7 @@ void EditorFileDialog::_favorite_move_down() { return; SWAP(favorited.write[a_idx], favorited.write[b_idx]); - EditorSettings::get_singleton()->set_favorite_dirs(favorited); + EditorSettings::get_singleton()->set_favorites(favorited); _update_favorites(); update_file_list(); @@ -1176,7 +1188,7 @@ void EditorFileDialog::_update_favorites() { favorite->set_pressed(false); - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < favorited.size(); i++) { bool cres = favorited[i].begins_with("res://"); if (cres != res) @@ -1206,7 +1218,7 @@ void EditorFileDialog::_favorite_toggled(bool p_toggle) { String cd = get_current_dir(); - Vector<String> favorited = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> favorited = EditorSettings::get_singleton()->get_favorites(); bool found = false; for (int i = 0; i < favorited.size(); i++) { @@ -1228,7 +1240,7 @@ void EditorFileDialog::_favorite_toggled(bool p_toggle) { favorite->set_pressed(true); } - EditorSettings::get_singleton()->set_favorite_dirs(favorited); + EditorSettings::get_singleton()->set_favorites(favorited); _update_favorites(); } diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 61eeff9162..56cefb9a47 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -190,8 +190,8 @@ private: void _save_to_recent(); //callback function is callback(String p_path,Ref<Texture> preview,Variant udata) preview null if could not load - void _thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); - void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _thumbnail_result(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); + void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); void _request_single_thumbnail(const String &p_path); void _unhandled_input(const Ref<InputEvent> &p_event); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 56358cf5b7..ee20d95f25 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1378,7 +1378,6 @@ void EditorFileSystem::update_script_classes() { ScriptServer::save_global_classes(); EditorNode::get_editor_data().script_class_save_icon_paths(); - emit_signal("script_classes_updated"); } void EditorFileSystem::_queue_update_script_classes() { @@ -1721,7 +1720,6 @@ void EditorFileSystem::_bind_methods() { ADD_SIGNAL(MethodInfo("filesystem_changed")); ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist"))); ADD_SIGNAL(MethodInfo("resources_reimported", PropertyInfo(Variant::POOL_STRING_ARRAY, "resources"))); - ADD_SIGNAL(MethodInfo("script_classes_updated")); } void EditorFileSystem::_update_extensions() { diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 728c4affbd..60040f641b 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -752,6 +752,8 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { } void EditorHelp::_update_doc() { + if (!doc->class_list.has(edited_class)) + return; scroll_locked = true; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d8e6f711ff..03746fb8b7 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -54,7 +54,6 @@ #include "editor/editor_audio_buses.h" #include "editor/editor_file_system.h" #include "editor/editor_help.h" -#include "editor/editor_initialize_ssl.h" #include "editor/editor_properties.h" #include "editor/editor_settings.h" #include "editor/editor_themes.h" @@ -2040,6 +2039,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { emit_signal("stop_pressed"); } break; + + case FILE_SHOW_IN_FILESYSTEM: { + String path = editor_data.get_scene_path(editor_data.get_edited_scene()); + if (path != String()) { + filesystem_dock->navigate_to_path(path); + } + } break; + case RUN_PLAY_SCENE: { _save_default_environment(); @@ -3358,19 +3365,12 @@ void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) { dock_slot[nrect]->show(); dock_select->update(); - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - - for (int i = 0; i < 4; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) @@ -3542,30 +3542,16 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p p_layout->set_value(p_section, "dock_filesystem_split", filesystem_dock->get_split_offset()); - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { - if (splits[i]->is_visible_in_tree()) { - p_layout->set_value(p_section, "dock_split_" + itos(i + 1), splits[i]->get_split_offset()); + if (vsplits[i]->is_visible_in_tree()) { + p_layout->set_value(p_section, "dock_split_" + itos(i + 1), vsplits[i]->get_split_offset()); } } - HSplitContainer *h_splits[4] = { - left_l_hsplit, - left_r_hsplit, - main_hsplit, - right_hsplit, - }; + for (int i = 0; i < hsplits.size(); i++) { - for (int i = 0; i < 4; i++) { - - p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), h_splits[i]->get_split_offset()); + p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset()); } } @@ -3611,21 +3597,14 @@ void EditorNode::_load_docks() { void EditorNode::_update_dock_slots_visibility() { - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - if (!docks_visible) { for (int i = 0; i < DOCK_SLOT_MAX; i++) { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { - splits[i]->hide(); + for (int i = 0; i < vsplits.size(); i++) { + vsplits[i]->hide(); } right_hsplit->hide(); @@ -3639,12 +3618,12 @@ void EditorNode::_update_dock_slots_visibility() { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } for (int i = 0; i < DOCK_SLOT_MAX; i++) { @@ -3665,12 +3644,6 @@ void EditorNode::_update_dock_slots_visibility() { void EditorNode::_dock_tab_changed(int p_tab) { // update visibility but don't set current tab - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; if (!docks_visible) { @@ -3678,8 +3651,8 @@ void EditorNode::_dock_tab_changed(int p_tab) { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { - splits[i]->hide(); + for (int i = 0; i < vsplits.size(); i++) { + vsplits[i]->hide(); } right_hsplit->hide(); @@ -3693,12 +3666,12 @@ void EditorNode::_dock_tab_changed(int p_tab) { dock_slot[i]->hide(); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } bottom_panel->show(); @@ -3757,42 +3730,28 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String } filesystem_dock->set_split_offset(fs_split_ofs); - VSplitContainer *splits[DOCK_SLOT_MAX / 2] = { - left_l_vsplit, - left_r_vsplit, - right_l_vsplit, - right_r_vsplit, - }; - - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { if (!p_layout->has_section_key(p_section, "dock_split_" + itos(i + 1))) continue; int ofs = p_layout->get_value(p_section, "dock_split_" + itos(i + 1)); - splits[i]->set_split_offset(ofs); + vsplits[i]->set_split_offset(ofs); } - HSplitContainer *h_splits[4] = { - left_l_hsplit, - left_r_hsplit, - main_hsplit, - right_hsplit, - }; - - for (int i = 0; i < 4; i++) { + for (int i = 0; i < hsplits.size(); i++) { if (!p_layout->has_section_key(p_section, "dock_hsplit_" + itos(i + 1))) continue; int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1)); - h_splits[i]->set_split_offset(ofs); + hsplits[i]->set_split_offset(ofs); } - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) { + for (int i = 0; i < vsplits.size(); i++) { bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count(); if (in_use) - splits[i]->show(); + vsplits[i]->show(); else - splits[i]->hide(); + vsplits[i]->hide(); } if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible()) @@ -3976,6 +3935,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES); if (scene_tabs->get_hovered_tab() >= 0) { scene_tabs_context_menu->add_separator(); + scene_tabs_context_menu->add_item(TTR("Show in filesystem"), FILE_SHOW_IN_FILESYSTEM); scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE); scene_tabs_context_menu->add_item(TTR("Close Tab"), FILE_CLOSE); } @@ -3990,7 +3950,7 @@ void EditorNode::_reposition_active_tab(int idx_to) { _update_scene_tabs(); } -void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { int p_tab = p_udata.operator signed int(); if (p_preview.is_valid()) { Rect2 rect = scene_tabs->get_tab_rect(p_tab); @@ -4686,7 +4646,6 @@ EditorNode::EditorNode() { SceneState::set_disable_placeholders(true); ResourceLoader::clear_translation_remaps(); //no remaps using during editor ResourceLoader::clear_path_remaps(); - editor_initialize_certificates(); //for asset sharing InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); @@ -4927,9 +4886,6 @@ EditorNode::EditorNode() { left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UL]); dock_slot[DOCK_SLOT_LEFT_BL] = memnew(TabContainer); left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BL]); - left_l_vsplit->hide(); - dock_slot[DOCK_SLOT_LEFT_UL]->hide(); - dock_slot[DOCK_SLOT_LEFT_BL]->hide(); left_r_hsplit = memnew(HSplitContainer); left_l_hsplit->add_child(left_r_hsplit); @@ -4967,19 +4923,22 @@ EditorNode::EditorNode() { right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UR]); dock_slot[DOCK_SLOT_RIGHT_BR] = memnew(TabContainer); right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]); - right_r_vsplit->hide(); - dock_slot[DOCK_SLOT_RIGHT_UR]->hide(); - dock_slot[DOCK_SLOT_RIGHT_BR]->hide(); - left_l_vsplit->connect("dragged", this, "_dock_split_dragged"); - left_r_vsplit->connect("dragged", this, "_dock_split_dragged"); - right_l_vsplit->connect("dragged", this, "_dock_split_dragged"); - right_r_vsplit->connect("dragged", this, "_dock_split_dragged"); + // Store them for easier access + vsplits.push_back(left_l_vsplit); + vsplits.push_back(left_r_vsplit); + vsplits.push_back(right_l_vsplit); + vsplits.push_back(right_r_vsplit); - left_l_hsplit->connect("dragged", this, "_dock_split_dragged"); - left_r_hsplit->connect("dragged", this, "_dock_split_dragged"); - main_hsplit->connect("dragged", this, "_dock_split_dragged"); - right_hsplit->connect("dragged", this, "_dock_split_dragged"); + hsplits.push_back(left_l_hsplit); + hsplits.push_back(left_r_hsplit); + hsplits.push_back(main_hsplit); + hsplits.push_back(right_hsplit); + + for (int i = 0; i < vsplits.size(); i++) { + vsplits[i]->connect("dragged", this, "_dock_split_dragged"); + hsplits[i]->connect("dragged", this, "_dock_split_dragged"); + } dock_select_popup = memnew(PopupPanel); gui_base->add_child(dock_select_popup); @@ -5489,63 +5448,72 @@ EditorNode::EditorNode() { _menu_option(SETTINGS_UPDATE_SPINNER_HIDE); } - scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data)); - dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(scene_tree_dock); - dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene")); - dock_slot[DOCK_SLOT_LEFT_BR]->hide(); + // Instantiate and place editor docks + scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data)); inspector_dock = memnew(InspectorDock(this, editor_data)); - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(inspector_dock); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector")); - - Button *property_editable_warning; - import_dock = memnew(ImportDock); - dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock); - dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(import_dock->get_index(), TTR("Import")); - - bool use_single_dock_column = (OS::get_singleton()->get_screen_size(OS::get_singleton()->get_current_screen()).x < 1200); - node_dock = memnew(NodeDock); - if (use_single_dock_column) { - dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock); - dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node")); - } else { - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(node_dock); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(node_dock->get_index(), TTR("Node")); - } filesystem_dock = memnew(FileSystemDock(this)); - filesystem_dock->set_file_list_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); - - if (use_single_dock_column) { - dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(filesystem_dock); - dock_slot[DOCK_SLOT_RIGHT_BL]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); - left_r_vsplit->hide(); - dock_slot[DOCK_SLOT_LEFT_UR]->hide(); - dock_slot[DOCK_SLOT_LEFT_BR]->hide(); - } else { - dock_slot[DOCK_SLOT_LEFT_UR]->add_child(filesystem_dock); - dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); - } + filesystem_dock->set_file_list_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/files_display_mode"))); filesystem_dock->connect("open", this, "open_request"); filesystem_dock->connect("instance", this, "_instance_request"); - const String docks_section = "docks"; + // Scene: Top left + dock_slot[DOCK_SLOT_LEFT_UR]->add_child(scene_tree_dock); + dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene")); + + // Import: Top left, behind Scene + dock_slot[DOCK_SLOT_LEFT_UR]->add_child(import_dock); + dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(import_dock->get_index(), TTR("Import")); + + // FileSystem: Bottom left + dock_slot[DOCK_SLOT_LEFT_BR]->add_child(filesystem_dock); + dock_slot[DOCK_SLOT_LEFT_BR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem")); + + // Inspector: Full height right + dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(inspector_dock); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector")); + + // Node: Full height right, behind Inspector + dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node")); + + // Hide unused dock slots and vsplits + dock_slot[DOCK_SLOT_LEFT_UL]->hide(); + dock_slot[DOCK_SLOT_LEFT_BL]->hide(); + dock_slot[DOCK_SLOT_RIGHT_BL]->hide(); + dock_slot[DOCK_SLOT_RIGHT_UR]->hide(); + dock_slot[DOCK_SLOT_RIGHT_BR]->hide(); + left_l_vsplit->hide(); + right_r_vsplit->hide(); + + // Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize + left_r_hsplit->set_split_offset(40 * EDSCALE); + main_hsplit->set_split_offset(-40 * EDSCALE); + // Define corresponding default layout + + const String docks_section = "docks"; overridden_default_layout = -1; default_layout.instance(); - default_layout->set_value(docks_section, "dock_3", "FileSystem"); - default_layout->set_value(docks_section, "dock_5", "Scene,Import"); - default_layout->set_value(docks_section, "dock_6", "Inspector,Node"); + // Dock numbers are based on DockSlot enum value + 1 + default_layout->set_value(docks_section, "dock_3", "Scene,Import"); + default_layout->set_value(docks_section, "dock_4", "FileSystem"); + default_layout->set_value(docks_section, "dock_5", "Inspector,Node"); - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) - default_layout->set_value(docks_section, "dock_hsplit_" + itos(i + 1), 0); - for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) + for (int i = 0; i < vsplits.size(); i++) default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0); + default_layout->set_value(docks_section, "dock_hsplit_1", 0); + default_layout->set_value(docks_section, "dock_hsplit_2", 40 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_3", -40 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_4", 0); _update_layouts_menu(); + // Bottom panels + bottom_panel = memnew(PanelContainer); bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer")); center_split->add_child(bottom_panel); @@ -5930,17 +5898,31 @@ bool EditorPluginList::forward_spatial_gui_input(Camera *p_camera, const Ref<Inp return discard; } -void EditorPluginList::forward_draw_over_viewport(Control *p_overlay) { +void EditorPluginList::forward_canvas_draw_over_viewport(Control *p_overlay) { + + for (int i = 0; i < plugins_list.size(); i++) { + plugins_list[i]->forward_canvas_draw_over_viewport(p_overlay); + } +} + +void EditorPluginList::forward_canvas_force_draw_over_viewport(Control *p_overlay) { + + for (int i = 0; i < plugins_list.size(); i++) { + plugins_list[i]->forward_canvas_force_draw_over_viewport(p_overlay); + } +} + +void EditorPluginList::forward_spatial_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < plugins_list.size(); i++) { - plugins_list[i]->forward_draw_over_viewport(p_overlay); + plugins_list[i]->forward_spatial_draw_over_viewport(p_overlay); } } -void EditorPluginList::forward_force_draw_over_viewport(Control *p_overlay) { +void EditorPluginList::forward_spatial_force_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < plugins_list.size(); i++) { - plugins_list[i]->forward_force_draw_over_viewport(p_overlay); + plugins_list[i]->forward_spatial_force_draw_over_viewport(p_overlay); } } diff --git a/editor/editor_node.h b/editor/editor_node.h index 7409c64a64..bdbe0a245b 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -125,6 +125,7 @@ private: FILE_SAVE_ALL_SCENES, FILE_SAVE_BEFORE_RUN, FILE_SAVE_AND_RUN, + FILE_SHOW_IN_FILESYSTEM, FILE_IMPORT_SUBSCENE, FILE_EXPORT_PROJECT, FILE_EXPORT_MESH_LIBRARY, @@ -208,7 +209,7 @@ private: String video_driver_request; void _video_driver_selected(int); - //split + // Split containers HSplitContainer *left_l_hsplit; VSplitContainer *left_l_vsplit; @@ -221,7 +222,11 @@ private: VSplitContainer *center_split; - //main tabs + // To access those easily by index + Vector<VSplitContainer *> vsplits; + Vector<HSplitContainer *> hsplits; + + // Main tabs Tabs *scene_tabs; PopupMenu *scene_tabs_context_menu; @@ -535,7 +540,7 @@ private: void _scene_tab_exit(); void _scene_tab_input(const Ref<InputEvent> &p_input); void _reposition_active_tab(int idx_to); - void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); void _scene_tab_script_edited(int p_tab); Dictionary _get_main_scene_state(); @@ -812,9 +817,11 @@ public: void make_visible(bool p_visible); void edit(Object *p_object); bool forward_gui_input(const Ref<InputEvent> &p_event); + void forward_canvas_draw_over_viewport(Control *p_overlay); + void forward_canvas_force_draw_over_viewport(Control *p_overlay); bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled); - void forward_draw_over_viewport(Control *p_overlay); - void forward_force_draw_over_viewport(Control *p_overlay); + void forward_spatial_draw_over_viewport(Control *p_overlay); + void forward_spatial_force_draw_over_viewport(Control *p_overlay); void add_plugin(EditorPlugin *p_plugin); void clear(); bool empty(); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 1f2e73654c..1ad23963a9 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -503,17 +503,17 @@ bool EditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; } -void EditorPlugin::forward_draw_over_viewport(Control *p_overlay) { +void EditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) { - if (get_script_instance() && get_script_instance()->has_method("forward_draw_over_viewport")) { - get_script_instance()->call("forward_draw_over_viewport", p_overlay); + if (get_script_instance() && get_script_instance()->has_method("forward_canvas_draw_over_viewport")) { + get_script_instance()->call("forward_canvas_draw_over_viewport", p_overlay); } } -void EditorPlugin::forward_force_draw_over_viewport(Control *p_overlay) { +void EditorPlugin::forward_canvas_force_draw_over_viewport(Control *p_overlay) { - if (get_script_instance() && get_script_instance()->has_method("forward_force_draw_over_viewport")) { - get_script_instance()->call("forward_force_draw_over_viewport", p_overlay); + if (get_script_instance() && get_script_instance()->has_method("forward_canvas_force_draw_over_viewport")) { + get_script_instance()->call("forward_canvas_force_draw_over_viewport", p_overlay); } } @@ -545,6 +545,20 @@ bool EditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEv return false; } + +void EditorPlugin::forward_spatial_draw_over_viewport(Control *p_overlay) { + + if (get_script_instance() && get_script_instance()->has_method("forward_spatial_draw_over_viewport")) { + get_script_instance()->call("forward_spatial_draw_over_viewport", p_overlay); + } +} + +void EditorPlugin::forward_spatial_force_draw_over_viewport(Control *p_overlay) { + + if (get_script_instance() && get_script_instance()->has_method("forward_spatial_force_draw_over_viewport")) { + get_script_instance()->call("forward_spatial_force_draw_over_viewport", p_overlay); + } +} String EditorPlugin::get_name() const { if (get_script_instance() && get_script_instance()->has_method("get_plugin_name")) { @@ -769,8 +783,8 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_spatial_gui_input", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_plugin_name")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::OBJECT, "get_plugin_icon")); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 6c385abf9b..e03aeb5d30 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -174,9 +174,13 @@ public: void notify_resource_saved(const Ref<Resource> &p_resource); virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); - virtual void forward_draw_over_viewport(Control *p_overlay); - virtual void forward_force_draw_over_viewport(Control *p_overlay); + virtual void forward_canvas_draw_over_viewport(Control *p_overlay); + virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay); + virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event); + virtual void forward_spatial_draw_over_viewport(Control *p_overlay); + virtual void forward_spatial_force_draw_over_viewport(Control *p_overlay); + virtual String get_name() const; virtual const Ref<Texture> get_icon() const; virtual bool has_main_screen() const; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 9982a31b7b..808a8ac2f8 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -336,16 +336,16 @@ void EditorPropertyArray::update_property() { } break; case Variant::INT: { - EditorPropertyInteger *ed = memnew(EditorPropertyInteger); - ed->setup(-100000, 100000, true, true); - prop = ed; + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + editor->setup(-100000, 100000, true, true); + prop = editor; } break; case Variant::REAL: { - EditorPropertyFloat *ed = memnew(EditorPropertyFloat); - ed->setup(-100000, 100000, 0.001, true, false, true, true); - prop = ed; + EditorPropertyFloat *editor = memnew(EditorPropertyFloat); + editor->setup(-100000, 100000, 0.001, true, false, true, true); + prop = editor; } break; case Variant::STRING: { @@ -357,63 +357,63 @@ void EditorPropertyArray::update_property() { case Variant::VECTOR2: { - EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::RECT2: { - EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::VECTOR3: { - EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM2D: { - EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::PLANE: { - EditorPropertyPlane *ed = memnew(EditorPropertyPlane); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::QUAT: { - EditorPropertyQuat *ed = memnew(EditorPropertyQuat); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyQuat *editor = memnew(EditorPropertyQuat); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::AABB: { - EditorPropertyAABB *ed = memnew(EditorPropertyAABB); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyAABB *editor = memnew(EditorPropertyAABB); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::BASIS: { - EditorPropertyBasis *ed = memnew(EditorPropertyBasis); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyBasis *editor = memnew(EditorPropertyBasis); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM: { - EditorPropertyTransform *ed = memnew(EditorPropertyTransform); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform *editor = memnew(EditorPropertyTransform); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; @@ -431,8 +431,9 @@ void EditorPropertyArray::update_property() { } break; case Variant::OBJECT: { - - prop = memnew(EditorPropertyResource); + EditorPropertyResource *editor = memnew(EditorPropertyResource); + editor->setup("Resource"); + prop = editor; } break; case Variant::DICTIONARY: { @@ -798,16 +799,16 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::INT: { - EditorPropertyInteger *ed = memnew(EditorPropertyInteger); - ed->setup(-100000, 100000, true, true); - prop = ed; + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + editor->setup(-100000, 100000, true, true); + prop = editor; } break; case Variant::REAL: { - EditorPropertyFloat *ed = memnew(EditorPropertyFloat); - ed->setup(-100000, 100000, 0.001, true, false, true, true); - prop = ed; + EditorPropertyFloat *editor = memnew(EditorPropertyFloat); + editor->setup(-100000, 100000, 0.001, true, false, true, true); + prop = editor; } break; case Variant::STRING: { @@ -815,67 +816,66 @@ void EditorPropertyDictionary::update_property() { } break; - // math types - + // math types case Variant::VECTOR2: { - EditorPropertyVector2 *ed = memnew(EditorPropertyVector2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector2 *editor = memnew(EditorPropertyVector2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::RECT2: { - EditorPropertyRect2 *ed = memnew(EditorPropertyRect2); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyRect2 *editor = memnew(EditorPropertyRect2); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::VECTOR3: { - EditorPropertyVector3 *ed = memnew(EditorPropertyVector3); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyVector3 *editor = memnew(EditorPropertyVector3); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM2D: { - EditorPropertyTransform2D *ed = memnew(EditorPropertyTransform2D); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::PLANE: { - EditorPropertyPlane *ed = memnew(EditorPropertyPlane); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyPlane *editor = memnew(EditorPropertyPlane); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::QUAT: { - EditorPropertyQuat *ed = memnew(EditorPropertyQuat); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyQuat *editor = memnew(EditorPropertyQuat); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::AABB: { - EditorPropertyAABB *ed = memnew(EditorPropertyAABB); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyAABB *editor = memnew(EditorPropertyAABB); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::BASIS: { - EditorPropertyBasis *ed = memnew(EditorPropertyBasis); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyBasis *editor = memnew(EditorPropertyBasis); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; case Variant::TRANSFORM: { - EditorPropertyTransform *ed = memnew(EditorPropertyTransform); - ed->setup(-100000, 100000, 0.001, true); - prop = ed; + EditorPropertyTransform *editor = memnew(EditorPropertyTransform); + editor->setup(-100000, 100000, 0.001, true); + prop = editor; } break; @@ -893,8 +893,9 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::OBJECT: { - - prop = memnew(EditorPropertyResource); + EditorPropertyResource *editor = memnew(EditorPropertyResource); + editor->setup("Resource"); + prop = editor; } break; case Variant::DICTIONARY: { @@ -902,39 +903,53 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::ARRAY: { - - prop = memnew(EditorPropertyArray); - + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::ARRAY); + prop = editor; } break; // arrays case Variant::POOL_BYTE_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_BYTE_ARRAY); + prop = editor; } break; case Variant::POOL_INT_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_INT_ARRAY); + prop = editor; } break; case Variant::POOL_REAL_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_REAL_ARRAY); + prop = editor; } break; case Variant::POOL_STRING_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_STRING_ARRAY); + prop = editor; } break; case Variant::POOL_VECTOR2_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_VECTOR2_ARRAY); + prop = editor; } break; case Variant::POOL_VECTOR3_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_VECTOR3_ARRAY); + prop = editor; } break; case Variant::POOL_COLOR_ARRAY: { - prop = memnew(EditorPropertyArray); + EditorPropertyArray *editor = memnew(EditorPropertyArray); + editor->setup(Variant::POOL_COLOR_ARRAY); + prop = editor; } break; default: {} } diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index edfee595ea..310c3b3a52 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -30,11 +30,14 @@ #include "editor_resource_preview.h" +#include "core/method_bind_ext.gen.inc" + #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/message_queue.h" #include "core/os/file_access.h" #include "core/project_settings.h" +#include "editor_node.h" #include "editor_scale.h" #include "editor_settings.h" @@ -46,32 +49,37 @@ bool EditorResourcePreviewGenerator::handles(const String &p_type) const { ERR_EXPLAIN("EditorResourcePreviewGenerator::handles needs to be overridden"); ERR_FAIL_V(false); } -Ref<Texture> EditorResourcePreviewGenerator::generate(const RES &p_from) const { + +Ref<Texture> EditorResourcePreviewGenerator::generate(const RES &p_from, const Size2 p_size) const { if (get_script_instance() && get_script_instance()->has_method("generate")) { - return get_script_instance()->call("generate", p_from); + return get_script_instance()->call("generate", p_from, p_size); } ERR_EXPLAIN("EditorResourcePreviewGenerator::generate needs to be overridden"); ERR_FAIL_V(Ref<Texture>()); } -Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_path) const { +Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_path, const Size2 p_size) const { if (get_script_instance() && get_script_instance()->has_method("generate_from_path")) { - return get_script_instance()->call("generate_from_path", p_path); + return get_script_instance()->call("generate_from_path", p_path, p_size); } RES res = ResourceLoader::load(p_path); if (!res.is_valid()) return res; - return generate(res); + return generate(res, p_size); +} + +bool EditorResourcePreviewGenerator::should_generate_small_preview() const { + return false; } void EditorResourcePreviewGenerator::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles", PropertyInfo(Variant::STRING, "type"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate", PropertyInfo(Variant::OBJECT, "from", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); - ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate_from_path", PropertyInfo(Variant::STRING, "path", PROPERTY_HINT_FILE))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate", PropertyInfo(Variant::OBJECT, "from", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::VECTOR2, "size"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(CLASS_INFO(Texture), "generate_from_path", PropertyInfo(Variant::STRING, "path", PROPERTY_HINT_FILE), PropertyInfo(Variant::VECTOR2, "size"))); } EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() { @@ -85,7 +93,7 @@ void EditorResourcePreview::_thread_func(void *ud) { erp->_thread(); } -void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Texture> &p_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) { +void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Texture> &p_texture, const Ref<Texture> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) { preview_mutex->lock(); @@ -103,6 +111,7 @@ void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Textur Item item; item.order = order++; item.preview = p_texture; + item.small_preview = p_small_texture; item.last_hash = hash; item.modified_time = modified_time; @@ -110,11 +119,10 @@ void EditorResourcePreview::_preview_ready(const String &p_str, const Ref<Textur preview_mutex->unlock(); - MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_ud); + MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_small_texture, p_ud); } -Ref<Texture> EditorResourcePreview::_generate_preview(const QueueItem &p_item, const String &cache_base) { - +void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base) { String type; if (p_item.resource.is_valid()) @@ -122,40 +130,59 @@ Ref<Texture> EditorResourcePreview::_generate_preview(const QueueItem &p_item, c else type = ResourceLoader::get_resource_type(p_item.path); - if (type == "") - return Ref<Texture>(); //could not guess type + if (type == "") { + r_texture = Ref<ImageTexture>(); + r_small_texture = Ref<ImageTexture>(); + return; //could not guess type + } - Ref<Texture> generated; + int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); + thumbnail_size *= EDSCALE; - for (int i = 0; i < preview_generators.size(); i++) { + r_texture = Ref<ImageTexture>(); + r_small_texture = Ref<ImageTexture>(); + for (int i = 0; i < preview_generators.size(); i++) { if (!preview_generators[i]->handles(type)) continue; + + Ref<Texture> generated; if (p_item.resource.is_valid()) { - generated = preview_generators[i]->generate(p_item.resource); + generated = preview_generators[i]->generate(p_item.resource, Vector2(thumbnail_size, thumbnail_size)); } else { - generated = preview_generators[i]->generate_from_path(p_item.path); + generated = preview_generators[i]->generate_from_path(p_item.path, Vector2(thumbnail_size, thumbnail_size)); } + r_texture = generated; + if (r_texture.is_valid() && preview_generators[i]->should_generate_small_preview()) { + int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_icon("Object", "EditorIcons")->get_width(); // Kind of a workaround to retreive the default icon size + small_thumbnail_size *= EDSCALE; + + Ref<Image> small_image = r_texture->get_data(); + small_image->resize(small_thumbnail_size, small_thumbnail_size, Image::INTERPOLATE_CUBIC); + r_small_texture.instance(); + r_small_texture->create_from_image(small_image); + } break; } if (!p_item.resource.is_valid()) { // cache the preview in case it's a resource on disk - if (generated.is_valid()) { - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; + if (r_texture.is_valid()) { //wow it generated a preview... save cache - ResourceSaver::save(cache_base + ".png", generated); + bool has_small_texture = r_small_texture.is_valid(); + ResourceSaver::save(cache_base + ".png", r_texture); + if (has_small_texture) { + ResourceSaver::save(cache_base + "_small.png", r_small_texture); + } FileAccess *f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE); f->store_line(itos(thumbnail_size)); + f->store_line(itos(has_small_texture)); f->store_line(itos(FileAccess::get_modified_time(p_item.path))); f->store_line(FileAccess::get_md5(p_item.path)); memdelete(f); } } - - return generated; } void EditorResourcePreview::_thread() { @@ -177,7 +204,7 @@ void EditorResourcePreview::_thread() { path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below) } - _preview_ready(path, cache[item.path].preview, item.id, item.function, item.userdata); + _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata); preview_mutex->unlock(); } else { @@ -185,15 +212,17 @@ void EditorResourcePreview::_thread() { preview_mutex->unlock(); Ref<ImageTexture> texture; + Ref<ImageTexture> small_texture; int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); thumbnail_size *= EDSCALE; if (item.resource.is_valid()) { - texture = _generate_preview(item, String()); + _generate_preview(texture, small_texture, item, String()); + //adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred - _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, item.id, item.function, item.userdata); + _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata); } else { @@ -207,12 +236,13 @@ void EditorResourcePreview::_thread() { FileAccess *f = FileAccess::open(file, FileAccess::READ); if (!f) { - //generate - texture = _generate_preview(item, cache_base); + // No cache found, generate + _generate_preview(texture, small_texture, item, cache_base); } else { uint64_t modtime = FileAccess::get_modified_time(item.path); int tsize = f->get_line().to_int64(); + bool has_small_texture = f->get_line().to_int(); uint64_t last_modtime = f->get_line().to_int64(); bool cache_valid = true; @@ -236,6 +266,7 @@ void EditorResourcePreview::_thread() { f = FileAccess::open(file, FileAccess::WRITE); f->store_line(itos(modtime)); + f->store_line(itos(has_small_texture)); f->store_line(md5); memdelete(f); } @@ -243,12 +274,12 @@ void EditorResourcePreview::_thread() { memdelete(f); } - //cache_valid = false; - if (cache_valid) { Ref<Image> img; img.instance(); + Ref<Image> small_img; + small_img.instance(); if (img->load(cache_base + ".png") != OK) { cache_valid = false; @@ -256,16 +287,24 @@ void EditorResourcePreview::_thread() { texture.instance(); texture->create_from_image(img, Texture::FLAG_FILTER); + + if (has_small_texture) { + if (small_img->load(cache_base + "_small.png") != OK) { + cache_valid = false; + } else { + small_texture.instance(); + small_texture->create_from_image(small_img, Texture::FLAG_FILTER); + } + } } } if (!cache_valid) { - texture = _generate_preview(item, cache_base); + _generate_preview(texture, small_texture, item, cache_base); } } - - _preview_ready(item.path, texture, item.id, item.function, item.userdata); + _preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata); } } @@ -287,7 +326,7 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p if (cache.has(path_id) && cache[path_id].last_hash == p_res->hash_edited_version()) { cache[path_id].order = order++; - p_receiver->call_deferred(p_receiver_func, path_id, cache[path_id].preview, p_userdata); + p_receiver->call_deferred(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, p_userdata); preview_mutex->unlock(); return; } @@ -312,7 +351,7 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object preview_mutex->lock(); if (cache.has(p_path)) { cache[p_path].order = order++; - p_receiver->call_deferred(p_receiver_func, p_path, cache[p_path].preview, p_userdata); + p_receiver->call_deferred(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, p_userdata); preview_mutex->unlock(); return; } diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 434b26e901..a3417cdf60 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -63,8 +63,10 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; - virtual Ref<Texture> generate_from_path(const String &p_path) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; + virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const; + + virtual bool should_generate_small_preview() const; EditorResourcePreviewGenerator(); }; @@ -92,6 +94,7 @@ class EditorResourcePreview : public Node { struct Item { Ref<Texture> preview; + Ref<Texture> small_preview; int order; uint32_t last_hash; uint64_t modified_time; @@ -101,8 +104,8 @@ class EditorResourcePreview : public Node { Map<String, Item> cache; - void _preview_ready(const String &p_str, const Ref<Texture> &p_texture, ObjectID id, const StringName &p_func, const Variant &p_ud); - Ref<Texture> _generate_preview(const QueueItem &p_item, const String &cache_base); + void _preview_ready(const String &p_str, const Ref<Texture> &p_texture, const Ref<Texture> &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud); + void _generate_preview(Ref<ImageTexture> &r_texture, Ref<ImageTexture> &r_small_texture, const QueueItem &p_item, const String &cache_base); static void _thread_func(void *ud); void _thread(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5d3c6dd087..29ce8d1830 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -30,6 +30,7 @@ #include "editor_settings.h" +#include "core/io/certs_compressed.gen.h" #include "core/io/compression.h" #include "core/io/config_file.h" #include "core/io/file_access_memory.h" @@ -510,17 +511,16 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("filesystem/file_dialog/show_hidden_files", false); _initial_set("filesystem/file_dialog/display_mode", 0); hints["filesystem/file_dialog/display_mode"] = PropertyInfo(Variant::INT, "filesystem/file_dialog/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); + _initial_set("filesystem/file_dialog/thumbnail_size", 64); hints["filesystem/file_dialog/thumbnail_size"] = PropertyInfo(Variant::INT, "filesystem/file_dialog/thumbnail_size", PROPERTY_HINT_RANGE, "32,128,16"); - _initial_set("docks/filesystem/disable_split", false); - _initial_set("docks/filesystem/split_mode_minimum_height", 600); _initial_set("docks/filesystem/display_mode", 0); - hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); + hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Tree only, Split"); _initial_set("docks/filesystem/thumbnail_size", 64); hints["docks/filesystem/thumbnail_size"] = PropertyInfo(Variant::INT, "docks/filesystem/thumbnail_size", PROPERTY_HINT_RANGE, "32,128,16"); - _initial_set("docks/filesystem/display_mode", 0); - hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); + _initial_set("docks/filesystem/files_display_mode", 0); + hints["docks/filesystem/files_display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/files_display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); _initial_set("docks/filesystem/always_show_folders", true); _initial_set("editors/animation/autorename_animation_tracks", true); @@ -947,6 +947,10 @@ void EditorSettings::setup_network() { _initial_set("network/debug/remote_port", port); add_property_hint(PropertyInfo(Variant::INT, "network/debug/remote_port", PROPERTY_HINT_RANGE, "1,65535,1")); + + // Editor SSL certificates override + _initial_set("network/ssl/editor_ssl_certificates", _SYSTEM_CERTS_PATH); + add_property_hint(PropertyInfo(Variant::STRING, "network/ssl/editor_ssl_certificates", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem")); } void EditorSettings::save() { @@ -1147,20 +1151,20 @@ Variant EditorSettings::get_project_metadata(const String &p_section, const Stri return cf->get_value(p_section, p_key, p_default); } -void EditorSettings::set_favorite_dirs(const Vector<String> &p_favorites_dirs) { +void EditorSettings::set_favorites(const Vector<String> &p_favorites) { - favorite_dirs = p_favorites_dirs; - FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorite_dirs"), FileAccess::WRITE); + favorites = p_favorites; + FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorites"), FileAccess::WRITE); if (f) { - for (int i = 0; i < favorite_dirs.size(); i++) - f->store_line(favorite_dirs[i]); + for (int i = 0; i < favorites.size(); i++) + f->store_line(favorites[i]); memdelete(f); } } -Vector<String> EditorSettings::get_favorite_dirs() const { +Vector<String> EditorSettings::get_favorites() const { - return favorite_dirs; + return favorites; } void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) { @@ -1181,11 +1185,11 @@ Vector<String> EditorSettings::get_recent_dirs() const { void EditorSettings::load_favorites() { - FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorite_dirs"), FileAccess::READ); + FileAccess *f = FileAccess::open(get_project_settings_dir().plus_file("favorites"), FileAccess::READ); if (f) { String line = f->get_line().strip_edges(); while (line != "") { - favorite_dirs.push_back(line); + favorites.push_back(line); line = f->get_line().strip_edges(); } memdelete(f); @@ -1212,18 +1216,25 @@ bool EditorSettings::is_dark_theme() { void EditorSettings::list_text_editor_themes() { String themes = "Adaptive,Default,Custom"; + DirAccess *d = DirAccess::open(get_text_editor_themes_dir()); if (d) { + List<String> custom_themes; d->list_dir_begin(); String file = d->get_next(); while (file != String()) { if (file.get_extension() == "tet" && file.get_basename().to_lower() != "default" && file.get_basename().to_lower() != "adaptive" && file.get_basename().to_lower() != "custom") { - themes += "," + file.get_basename(); + custom_themes.push_back(file.get_basename()); } file = d->get_next(); } d->list_dir_end(); memdelete(d); + + custom_themes.sort(); + for (List<String>::Element *E = custom_themes.front(); E; E = E->next()) { + themes += "," + E->get(); + } } add_property_hint(PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, themes)); } @@ -1466,8 +1477,8 @@ void EditorSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("set_project_metadata", "section", "key", "data"), &EditorSettings::set_project_metadata); ClassDB::bind_method(D_METHOD("get_project_metadata", "section", "key", "default"), &EditorSettings::get_project_metadata, DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("set_favorite_dirs", "dirs"), &EditorSettings::set_favorite_dirs); - ClassDB::bind_method(D_METHOD("get_favorite_dirs"), &EditorSettings::get_favorite_dirs); + ClassDB::bind_method(D_METHOD("set_favorites", "dirs"), &EditorSettings::set_favorites); + ClassDB::bind_method(D_METHOD("get_favorites"), &EditorSettings::get_favorites); ClassDB::bind_method(D_METHOD("set_recent_dirs", "dirs"), &EditorSettings::set_recent_dirs); ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 8165c36e67..7b0de9617c 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -107,7 +107,7 @@ private: String config_file_path; String project_config_dir; - Vector<String> favorite_dirs; + Vector<String> favorites; Vector<String> recent_dirs; bool save_changed_setting; @@ -173,8 +173,8 @@ public: void set_project_metadata(const String &p_section, const String &p_key, Variant p_data); Variant get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const; - void set_favorite_dirs(const Vector<String> &p_favorites_dirs); - Vector<String> get_favorite_dirs() const; + void set_favorites(const Vector<String> &p_favorites); + Vector<String> get_favorites() const; void set_recent_dirs(const Vector<String> &p_recent_dirs); Vector<String> get_recent_dirs() const; void load_favorites(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 3a55966e7b..46f89439c0 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -40,27 +40,38 @@ #include "editor_settings.h" #include "scene/main/viewport.h" +Ref<Texture> FileSystemDock::_get_tree_item_icon(EditorFileSystemDirectory *p_dir, int p_idx) { + Ref<Texture> file_icon; + if (!p_dir->get_file_import_is_valid(p_idx)) { + file_icon = get_icon("ImportFail", "EditorIcons"); + } else { + String file_type = p_dir->get_file_type(p_idx); + file_icon = (has_icon(file_type, "EditorIcons")) ? get_icon(file_type, "EditorIcons") : get_icon("File", "EditorIcons"); + } + return file_icon; +} + bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths) { - TreeItem *item = tree->create_item(p_parent); + bool parent_should_expand = false; + + // Create a tree item for the subdirectory + TreeItem *subdirectory_item = tree->create_item(p_parent); String dname = p_dir->get_name(); if (dname == "") dname = "res://"; - item->set_text(0, dname); - item->set_icon(0, get_icon("Folder", "EditorIcons")); - item->set_selectable(0, true); + subdirectory_item->set_text(0, dname); + subdirectory_item->set_icon(0, get_icon("Folder", "EditorIcons")); + subdirectory_item->set_selectable(0, true); String lpath = p_dir->get_path(); - if (lpath != "res://" && lpath.ends_with("/")) { - lpath = lpath.substr(0, lpath.length() - 1); - } - item->set_metadata(0, lpath); - if (lpath == path) { - item->select(0); + subdirectory_item->set_metadata(0, lpath); + if (path == lpath || ((display_mode_setting == DISPLAY_MODE_SETTING_SPLIT) && path.get_base_dir() == lpath)) { + subdirectory_item->select(0); } if ((path.begins_with(lpath) && path != lpath)) { - item->set_collapsed(false); + subdirectory_item->set_collapsed(false); } else { bool is_collapsed = true; for (int i = 0; i < uncollapsed_paths.size(); i++) { @@ -69,23 +80,65 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory break; } } - item->set_collapsed(is_collapsed); + subdirectory_item->set_collapsed(is_collapsed); + } + if (searched_string.length() > 0 && dname.to_lower().find(searched_string) >= 0) { + parent_should_expand = true; } + // Create items for all subdirectories for (int i = 0; i < p_dir->get_subdir_count(); i++) - _create_tree(item, p_dir->get_subdir(i), uncollapsed_paths); + parent_should_expand = (_create_tree(subdirectory_item, p_dir->get_subdir(i), uncollapsed_paths) || parent_should_expand); - return true; -} + // Create all items for the files in the subdirectory + if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { + for (int i = 0; i < p_dir->get_file_count(); i++) { + String file_name = p_dir->get_file(i); + + if (searched_string.length() > 0) { + if (file_name.to_lower().find(searched_string) < 0) { + // The seached string is not in the file name, we skip it + continue; + } else { + // We expand all parents + parent_should_expand = true; + } + } -void FileSystemDock::_update_tree(bool keep_collapse_state, bool p_uncollapse_root) { + TreeItem *file_item = tree->create_item(subdirectory_item); + file_item->set_text(0, file_name); + file_item->set_icon(0, _get_tree_item_icon(p_dir, i)); + String file_metadata = lpath.plus_file(file_name); + file_item->set_metadata(0, file_metadata); + if (path == file_metadata) { + file_item->select(0); + file_item->set_as_cursor(0); + } + Array udata; + udata.push_back(tree_update_id); + udata.push_back(file_item); + EditorResourcePreview::get_singleton()->queue_resource_preview(file_metadata, this, "_tree_thumbnail_done", udata); + } + } - Vector<String> uncollapsed_paths; - if (keep_collapse_state) { - TreeItem *root = tree->get_root(); - if (root) { - TreeItem *resTree = root->get_children()->get_next(); + if (searched_string.length() > 0) { + if (parent_should_expand) { + subdirectory_item->set_collapsed(false); + } else if (dname != "res://") { + subdirectory_item->get_parent()->remove_child(subdirectory_item); + } + } + + return parent_should_expand; +} +Vector<String> FileSystemDock::_compute_uncollapsed_paths() { + // Register currently collapsed paths + Vector<String> uncollapsed_paths; + TreeItem *root = tree->get_root(); + if (root) { + TreeItem *resTree = root->get_children()->get_next(); + if (resTree) { Vector<TreeItem *> needs_check; needs_check.push_back(resTree); @@ -102,84 +155,121 @@ void FileSystemDock::_update_tree(bool keep_collapse_state, bool p_uncollapse_ro } } } + return uncollapsed_paths; +} + +void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool p_uncollapse_root) { + // Recreate the tree tree->clear(); + tree_update_id++; updating_tree = true; - TreeItem *root = tree->create_item(); + + // Handles the favorites TreeItem *favorites = tree->create_item(root); favorites->set_icon(0, get_icon("Favorites", "EditorIcons")); favorites->set_text(0, TTR("Favorites:")); favorites->set_selectable(0, false); - Vector<String> favorite_paths = EditorSettings::get_singleton()->get_favorite_dirs(); - String res_path = "res://"; - Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); + Vector<String> favorite_paths = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < favorite_paths.size(); i++) { String fave = favorite_paths[i]; - if (!fave.begins_with(res_path)) + if (!fave.begins_with("res://")) + continue; + if (display_mode_setting == DISPLAY_MODE_SETTING_SPLIT && !fave.ends_with("/")) continue; - TreeItem *ti = tree->create_item(favorites); - if (fave == res_path) - ti->set_text(0, "/"); - else - ti->set_text(0, fave.get_file()); - ti->set_icon(0, folder_icon); - ti->set_selectable(0, true); - ti->set_metadata(0, fave); + Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); + + String text; + Ref<Texture> icon; + if (fave == "res://") { + text = "/"; + icon = folder_icon; + } else if (fave.ends_with("/")) { + text = fave.substr(0, fave.length() - 1).get_file(); + icon = folder_icon; + } else { + text = fave.get_file(); + int index; + EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->find_file(fave, &index); + if (dir) { + icon = _get_tree_item_icon(dir, index); + } else { + icon = get_icon("File", "EditorIcons"); + } + } + + if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) { + TreeItem *ti = tree->create_item(favorites); + ti->set_text(0, text); + ti->set_icon(0, icon); + ti->set_tooltip(0, fave); + ti->set_selectable(0, true); + ti->set_metadata(0, fave); + } } + Vector<String> uncollapsed_paths = p_uncollapsed_paths; if (p_uncollapse_root) { uncollapsed_paths.push_back("res://"); } + // Create the remaining of the tree _create_tree(root, EditorFileSystem::get_singleton()->get_filesystem(), uncollapsed_paths); tree->ensure_cursor_is_visible(); updating_tree = false; } void FileSystemDock::_update_display_mode() { - - bool disable_split = bool(EditorSettings::get_singleton()->get("docks/filesystem/disable_split")); - bool compact_mode = get_size().height < int(EditorSettings::get_singleton()->get("docks/filesystem/split_mode_minimum_height")); - DisplayMode new_mode; - if (disable_split || compact_mode) { - new_mode = file_list_view ? DISPLAY_FILE_LIST_ONLY : DISPLAY_TREE_ONLY; + // Compute the new display mode + DisplayMode new_display_mode; + if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { + new_display_mode = file_list_view ? DISPLAY_MODE_FILE_LIST_ONLY : DISPLAY_MODE_TREE_ONLY; } else { - new_mode = DISPLAY_SPLIT; + new_display_mode = DISPLAY_MODE_SPLIT; } - if (new_mode != display_mode) { - switch (new_mode) { - case DISPLAY_TREE_ONLY: + if (new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) { + display_mode = new_display_mode; + old_display_mode_setting = display_mode_setting; + button_toggle_display_mode->set_pressed(display_mode_setting == DISPLAY_MODE_SETTING_SPLIT ? true : false); + switch (display_mode) { + case DISPLAY_MODE_TREE_ONLY: tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); - _update_tree(true); + if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { + tree_search_box->show(); + } else { + tree_search_box->hide(); + } + _update_tree(_compute_uncollapsed_paths()); file_list_vb->hide(); break; - case DISPLAY_FILE_LIST_ONLY: + case DISPLAY_MODE_FILE_LIST_ONLY: tree->hide(); + tree_search_box->hide(); button_tree->show(); file_list_vb->show(); _update_files(true); break; - case DISPLAY_SPLIT: + case DISPLAY_MODE_SPLIT: tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); button_tree->hide(); tree->ensure_cursor_is_visible(); - _update_tree(true); + tree_search_box->hide(); + _update_tree(_compute_uncollapsed_paths()); file_list_vb->show(); _update_files(true); break; } - display_mode = new_mode; } } @@ -201,34 +291,36 @@ void FileSystemDock::_notification(int p_what) { String ei = "EditorIcons"; button_reload->set_icon(get_icon("Reload", ei)); - button_favorite->set_icon(get_icon("Favorites", ei)); - //button_instance->set_icon(get_icon("Add", ei)); - //button_open->set_icon(get_icon("Folder", ei)); + button_toggle_display_mode->set_icon(get_icon("Panels2", ei)); button_tree->set_icon(get_icon("Filesystem", ei)); _update_file_list_display_mode_button(); button_file_list_display_mode->connect("pressed", this, "_change_file_display"); - //file_options->set_icon( get_icon("Tools","ei")); - files->connect("item_activated", this, "_select_file"); + + files->connect("item_activated", this, "_file_list_activate_file"); button_hist_next->connect("pressed", this, "_fw_history"); button_hist_prev->connect("pressed", this, "_bw_history"); - search_box->set_right_icon(get_icon("Search", ei)); - search_box->set_clear_button_enabled(true); + tree_search_box->set_right_icon(get_icon("Search", ei)); + tree_search_box->set_clear_button_enabled(true); + file_list_search_box->set_right_icon(get_icon("Search", ei)); + file_list_search_box->set_clear_button_enabled(true); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); - button_show->set_icon(get_icon("GuiVisibilityVisible", "EditorIcons")); - file_options->connect("id_pressed", this, "_file_option"); - folder_options->connect("id_pressed", this, "_folder_option"); + file_list_popup->connect("id_pressed", this, "_file_list_rmb_option"); + tree_popup->connect("id_pressed", this, "_tree_rmb_option"); button_tree->connect("pressed", this, "_go_to_tree", varray(), CONNECT_DEFERRED); current_path->connect("text_entered", this, "navigate_to_path"); + display_mode_setting = DisplayModeSetting(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); + always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders")); + _update_display_mode(); if (EditorFileSystem::get_singleton()->is_scanning()) { _set_scanning_mode(); } else { - _update_tree(false, true); + _update_tree(Vector<String>(), true); } } break; @@ -245,7 +337,7 @@ void FileSystemDock::_notification(int p_what) { Dictionary dd = get_viewport()->gui_get_drag_data(); if (tree->is_visible_in_tree() && dd.has("type")) { if ((String(dd["type"]) == "files") || (String(dd["type"]) == "files_and_dirs") || (String(dd["type"]) == "resource")) { - tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM | Tree::DROP_MODE_INBETWEEN); } else if ((String(dd["type"]) == "favorite")) { tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); } @@ -261,20 +353,37 @@ void FileSystemDock::_notification(int p_what) { // Update icons String ei = "EditorIcons"; button_reload->set_icon(get_icon("Reload", ei)); - button_favorite->set_icon(get_icon("Favorites", ei)); + button_toggle_display_mode->set_icon(get_icon("Panels2", ei)); button_tree->set_icon(get_icon("Filesystem", ei)); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); - search_box->set_right_icon(get_icon("Search", ei)); - search_box->set_clear_button_enabled(true); + tree_search_box->set_right_icon(get_icon("Search", ei)); + tree_search_box->set_clear_button_enabled(true); + file_list_search_box->set_right_icon(get_icon("Search", ei)); + file_list_search_box->set_clear_button_enabled(true); + + bool should_update_files = false; - // Change size mode - int new_file_list_mode = int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode")); + // Update file list display mode + int new_file_list_mode = int(EditorSettings::get_singleton()->get("docks/filesystem/files_display_mode")); if (new_file_list_mode != file_list_display_mode) { set_file_list_display_mode(new_file_list_mode); - } else { _update_file_list_display_mode_button(); + should_update_files = true; + } + + // Update display of files in tree + display_mode_setting = DisplayModeSetting(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); + + // Update allways showfolders + bool new_always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders")); + if (new_always_show_folders != always_show_folders) { + always_show_folders = new_always_show_folders; + should_update_files = true; + } + + if (should_update_files) { _update_files(true); } @@ -285,74 +394,33 @@ void FileSystemDock::_notification(int p_what) { } } -void FileSystemDock::_dir_selected() { +void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_selected) { + // Return if we don't select something new + if (!p_selected) + return; + + // Tree item selected TreeItem *sel = tree->get_selected(); if (!sel) return; path = sel->get_metadata(0); - bool found = false; - Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs(); - for (int i = 0; i < favorites.size(); i++) { - - if (favorites[i] == path) { - found = true; - break; - } - } - - button_favorite->set_pressed(found); + // Set the current path current_path->set_text(path); _push_to_history(); - if (display_mode == DISPLAY_SPLIT) { + // Update the file list + if (!updating_tree && display_mode == DISPLAY_MODE_SPLIT) { _update_files(false); } } -void FileSystemDock::_favorites_pressed() { - - TreeItem *sel = tree->get_selected(); - if (!sel) - return; - path = sel->get_metadata(0); - - int idx = -1; - Vector<String> favorites = EditorSettings::get_singleton()->get_favorite_dirs(); - for (int i = 0; i < favorites.size(); i++) { - - if (favorites[i] == path) { - idx = i; - break; - } - } - - if (idx == -1) { - favorites.push_back(path); - } else { - favorites.remove(idx); - } - EditorSettings::get_singleton()->set_favorite_dirs(favorites); - _update_tree(true); -} - -void FileSystemDock::_show_current_scene_file() { - - int index = EditorNode::get_editor_data().get_edited_scene(); - String path = EditorNode::get_editor_data().get_scene_path(index); - if (path != String()) { - navigate_to_path(path); - } -} - String FileSystemDock::get_selected_path() const { - - TreeItem *sel = tree->get_selected(); - if (!sel) - return ""; - - return sel->get_metadata(0); + if (path.ends_with("/")) + return path; + else + return path.get_base_dir(); } String FileSystemDock::get_current_path() const { @@ -361,14 +429,17 @@ String FileSystemDock::get_current_path() const { } void FileSystemDock::navigate_to_path(const String &p_path) { + + String target_path = p_path; // If the path is a file, do not only go to the directory in the tree, also select the file in the file list. - String file_name = ""; + if (target_path.ends_with("/")) { + target_path = target_path.substr(0, target_path.length() - 1); + } DirAccess *dirAccess = DirAccess::open("res://"); if (dirAccess->file_exists(p_path)) { - path = p_path.get_base_dir(); - file_name = p_path.get_file(); + path = target_path; } else if (dirAccess->dir_exists(p_path)) { - path = p_path; + path = target_path + "/"; } else { ERR_EXPLAIN(vformat(TTR("Cannot navigate to '%s' as it has not been found in the file system!"), p_path)); ERR_FAIL(); @@ -377,13 +448,20 @@ void FileSystemDock::navigate_to_path(const String &p_path) { current_path->set_text(path); _push_to_history(); - if (display_mode == DISPLAY_SPLIT) { - _update_tree(true); + if (display_mode == DISPLAY_MODE_SPLIT) { + _update_tree(_compute_uncollapsed_paths()); _update_files(false); - } else { - _go_to_file_list(); + } else if (display_mode == DISPLAY_MODE_TREE_ONLY) { + if (path.ends_with("/")) { + _go_to_file_list(); + } else { + _update_tree(_compute_uncollapsed_paths()); + } + } else { // DISPLAY_MODE_FILE_LIST_ONLY + _update_files(true); } + String file_name = p_path.get_file(); if (!file_name.empty()) { for (int i = 0; i < files->get_item_count(); i++) { if (files->get_item_text(i) == file_name) { @@ -395,10 +473,9 @@ void FileSystemDock::navigate_to_path(const String &p_path) { } } -void FileSystemDock::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata) { +void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { if ((file_list_vb->is_visible_in_tree() || path == p_path.get_base_dir()) && p_preview.is_valid()) { - Array uarr = p_udata; int idx = uarr[0]; String file = uarr[1]; @@ -407,6 +484,27 @@ void FileSystemDock::_thumbnail_done(const String &p_path, const Ref<Texture> &p } } +void FileSystemDock::_tree_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) { + if (p_small_preview.is_valid()) { + Array uarr = p_udata; + if (tree_update_id == (int)uarr[0]) { + TreeItem *file_item = Object::cast_to<TreeItem>(uarr[1]); + if (file_item) { + file_item->set_icon(0, p_small_preview); + + // Update the favorite icon if needed + TreeItem *favorite = tree->get_root()->get_children()->get_children(); + while (favorite) { + if (favorite->get_metadata(0) == file_item->get_metadata(0)) { + favorite->set_icon(0, p_small_preview); + } + favorite = favorite->get_next(); + } + } + } + } +} + void FileSystemDock::_update_file_list_display_mode_button() { if (button_file_list_display_mode->is_pressed()) { @@ -424,7 +522,7 @@ void FileSystemDock::_change_file_display() { _update_file_list_display_mode_button(); - EditorSettings::get_singleton()->set("docks/filesystem/display_mode", file_list_display_mode); + EditorSettings::get_singleton()->set("docks/filesystem/files_display_mode", file_list_display_mode); _update_files(true); } @@ -438,12 +536,10 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> * _search(p_path->get_subdir(i), matches, p_max_items); } - String match = search_box->get_text().to_lower(); - for (int i = 0; i < p_path->get_file_count(); i++) { String file = p_path->get_file(i); - if (file.to_lower().find(match) != -1) { + if (file.to_lower().find(searched_string) != -1) { FileInfo fi; fi.name = file; @@ -461,12 +557,10 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> * void FileSystemDock::_update_files(bool p_keep_selection) { + // Register the previously selected items Set<String> cselection; - if (p_keep_selection) { - for (int i = 0; i < files->get_item_count(); i++) { - if (files->is_selected(i)) cselection.insert(files->get_item_text(i)); } @@ -476,7 +570,17 @@ void FileSystemDock::_update_files(bool p_keep_selection) { current_path->set_text(path); - EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(path); + String directory = path; + if (directory.ends_with("/") && directory != "res://") { + directory = directory.substr(0, directory.length() - 1); + } + String file = ""; + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); + if (!efd) { + directory = path.get_base_dir(); + file = path.get_file(); + efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); + } if (!efd) return; @@ -487,10 +591,8 @@ void FileSystemDock::_update_files(bool p_keep_selection) { Ref<Texture> file_thumbnail; Ref<Texture> file_thumbnail_broken; - bool always_show_folders = EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders"); - bool use_thumbnails = (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS); - bool use_folders = search_box->get_text().length() == 0 && ((display_mode == DISPLAY_FILE_LIST_ONLY || display_mode == DISPLAY_TREE_ONLY) || always_show_folders); + bool use_folders = searched_string.length() == 0 && ((display_mode == DISPLAY_MODE_FILE_LIST_ONLY || display_mode == DISPLAY_MODE_TREE_ONLY) || always_show_folders); if (use_thumbnails) { @@ -521,14 +623,15 @@ void FileSystemDock::_update_files(bool p_keep_selection) { if (use_folders) { Ref<Texture> folderIcon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); - if (path != "res://") { + if (directory != "res://") { files->add_item("..", folderIcon, true); - String bd = path.get_base_dir(); + String bd = directory.get_base_dir(); if (bd != "res://" && !bd.ends_with("/")) bd += "/"; files->set_item_metadata(files->get_item_count() - 1, bd); + files->set_item_selectable(files->get_item_count() - 1, false); } for (int i = 0; i < efd->get_subdir_count(); i++) { @@ -536,16 +639,17 @@ void FileSystemDock::_update_files(bool p_keep_selection) { String dname = efd->get_subdir(i)->get_name(); files->add_item(dname, folderIcon, true); - files->set_item_metadata(files->get_item_count() - 1, path.plus_file(dname) + "/"); + files->set_item_metadata(files->get_item_count() - 1, directory.plus_file(dname) + "/"); - if (cselection.has(dname)) + if (cselection.has(dname)) { files->select(files->get_item_count() - 1, false); + } } } List<FileInfo> filelist; - if (search_box->get_text().length() > 0) { + if (searched_string.length() > 0) { _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); filelist.sort(); @@ -555,7 +659,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { FileInfo fi; fi.name = efd->get_file(i); - fi.path = path.plus_file(fi.name); + fi.path = directory.plus_file(fi.name); fi.type = efd->get_file_type(i); fi.import_broken = !efd->get_file_import_is_valid(i); fi.import_status = 0; @@ -598,7 +702,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { udata.resize(2); udata[0] = item_index; udata[1] = fname; - EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_thumbnail_done", udata); + EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata); } } else { files->add_item(fname, type_icon, true); @@ -609,6 +713,11 @@ void FileSystemDock::_update_files(bool p_keep_selection) { if (cselection.has(fname)) files->select(item_index, false); + if (!p_keep_selection && file != "" && fname == file) { + files->select(item_index, true); + files->ensure_current_is_visible(); + } + if (finfo->sources.size()) { for (int j = 0; j < finfo->sources.size(); j++) { tooltip += "\nSource: " + finfo->sources[j]; @@ -618,8 +727,8 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } } -void FileSystemDock::_select_file(int p_idx) { - String fpath = files->get_item_metadata(p_idx); +void FileSystemDock::_select_file(const String p_path) { + String fpath = p_path; if (fpath.ends_with("/")) { if (fpath != "res://") { fpath = fpath.substr(0, fpath.length() - 1); @@ -634,9 +743,21 @@ void FileSystemDock::_select_file(int p_idx) { } } +void FileSystemDock::_tree_activate_file() { + TreeItem *selected = tree->get_selected(); + if (selected) { + call_deferred("_select_file", selected->get_metadata(0)); + } +} + +void FileSystemDock::_file_list_activate_file(int p_idx) { + _select_file(files->get_item_metadata(p_idx)); +} + void FileSystemDock::_go_to_file_list() { - if (display_mode == DISPLAY_TREE_ONLY) { + if (display_mode == DISPLAY_MODE_TREE_ONLY) { + file_list_view = true; _update_display_mode(); } else { @@ -645,6 +766,7 @@ void FileSystemDock::_go_to_file_list() { _update_files(false); } } + void FileSystemDock::_go_to_tree() { file_list_view = false; @@ -655,7 +777,7 @@ void FileSystemDock::_go_to_tree() { void FileSystemDock::_preview_invalidated(const String &p_path) { - if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && search_box->get_text() == String() && file_list_vb->is_visible_in_tree()) { + if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && searched_string.length() == 0 && file_list_vb->is_visible_in_tree()) { for (int i = 0; i < files->get_item_count(); i++) { @@ -665,7 +787,7 @@ void FileSystemDock::_preview_invalidated(const String &p_path) { udata.resize(2); udata[0] = i; udata[1] = files->get_item_text(i); - EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, this, "_thumbnail_done", udata); + EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, this, "_file_list_thumbnail_done", udata); break; } } @@ -680,7 +802,7 @@ void FileSystemDock::_fs_changed() { split_box->show(); if (tree->is_visible()) { - _update_tree(true); + _update_tree(_compute_uncollapsed_paths()); } if (file_list_vb->is_visible()) { @@ -724,7 +846,7 @@ void FileSystemDock::_update_history() { current_path->set_text(path); if (tree->is_visible()) { - _update_tree(true); + _update_tree(_compute_uncollapsed_paths()); tree->grab_focus(); tree->ensure_cursor_is_visible(); } @@ -968,23 +1090,23 @@ void FileSystemDock::_update_project_settings_after_move(const Map<String, Strin ProjectSettings::get_singleton()->save(); } -void FileSystemDock::_update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const { - - Vector<String> favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs(); - Vector<String> new_favorite_dirs; +void FileSystemDock::_update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const { - for (int i = 0; i < favorite_dirs.size(); i++) { - String old_path = favorite_dirs[i] + "/"; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorites; - if (p_renames.has(old_path)) { - String new_path = p_renames[old_path]; - new_favorite_dirs.push_back(new_path.substr(0, new_path.length() - 1)); + for (int i = 0; i < favorites.size(); i++) { + String old_path = favorites[i]; + if (p_folders_renames.has(old_path)) { + new_favorites.push_back(p_folders_renames[old_path]); + } else if (p_files_renames.has(old_path)) { + new_favorites.push_back(p_files_renames[old_path]); } else { - new_favorite_dirs.push_back(favorite_dirs[i]); + new_favorites.push_back(old_path); } } - EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs); + EditorSettings::get_singleton()->set_favorites(new_favorites); } void FileSystemDock::_make_dir_confirm() { @@ -998,9 +1120,13 @@ void FileSystemDock::_make_dir_confirm() { return; } - print_verbose("Making folder " + dir_name + " in " + path); + String directory = path; + if (!directory.ends_with("/")) { + directory = directory.get_base_dir(); + } + print_verbose("Making folder " + dir_name + " in " + directory); DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - Error err = da->change_dir(path); + Error err = da->change_dir(directory); if (err == OK) { err = da->make_dir(dir_name); } @@ -1051,7 +1177,7 @@ void FileSystemDock::_rename_operation_confirm() { _update_dependencies_after_move(file_renames); _update_resource_paths_after_move(file_renames); _update_project_settings_after_move(file_renames); - _update_favorite_dirs_list_after_move(folder_renames); + _update_favorites_list_after_move(file_renames, folder_renames); //Rescan everything print_verbose("FileSystem: calling rescan."); @@ -1144,111 +1270,209 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw _update_dependencies_after_move(file_renames); _update_resource_paths_after_move(file_renames); _update_project_settings_after_move(file_renames); - _update_favorite_dirs_list_after_move(folder_renames); + _update_favorites_list_after_move(file_renames, folder_renames); print_verbose("FileSystem: calling rescan."); _rescan(); } } -void FileSystemDock::_file_option(int p_option) { +Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion) { + // Build a list of selected items with the active one at the first position + Vector<String> selected_strings; + + TreeItem *active_selected = tree->get_selected(); + if (active_selected) { + selected_strings.push_back(active_selected->get_metadata(0)); + } + + TreeItem *selected = tree->get_root(); + selected = tree->get_next_selected(selected); + while (selected) { + if (selected != active_selected) { + selected_strings.push_back(selected->get_metadata(0)); + } + selected = tree->get_next_selected(selected); + } + + // Remove paths or files that are included into another + if (remove_self_inclusion && selected_strings.size() > 1) { + selected_strings.sort_custom<NaturalNoCaseComparator>(); + String last_path = ""; + for (int i = 0; i < selected_strings.size(); i++) { + if (last_path != "" && selected_strings[i].begins_with(last_path)) { + selected_strings.remove(i); + i--; + } + if (selected_strings[i].ends_with("/")) { + last_path = selected_strings[i]; + } + } + } + return selected_strings; +} + +void FileSystemDock::_tree_rmb_option(int p_option) { + + Vector<String> selected_strings = _tree_get_selected(); + + // Execute the current option switch (p_option) { - case FILE_SHOW_IN_EXPLORER: { + case FOLDER_EXPAND_ALL: + case FOLDER_COLLAPSE_ALL: { + // Expand or collapse the folder + if (selected_strings.size() == 1) { + bool is_collapsed = (p_option == FOLDER_COLLAPSE_ALL); + + Vector<TreeItem *> needs_check; + needs_check.push_back(tree->get_selected()); - String path = this->path; + while (needs_check.size()) { + needs_check[0]->set_collapsed(is_collapsed); - // first try to grab directory from selected file, so that it works for searched files - int idx = files->get_current(); + TreeItem *child = needs_check[0]->get_children(); + while (child) { + needs_check.push_back(child); + child = child->get_next(); + } - if (idx >= 0 && idx < files->get_item_count()) { - path = files->get_item_metadata(idx); - path = path.get_base_dir(); + needs_check.remove(0); + } } + } break; + default: { + _file_option(p_option, selected_strings); + } break; + } +} + +void FileSystemDock::_file_list_rmb_option(int p_option) { + Vector<int> selected_id = files->get_selected_items(); + Vector<String> selected; + for (int i = 0; i < selected_id.size(); i++) { + selected.push_back(files->get_item_metadata(selected_id[i])); + } + _file_option(p_option, selected); +} - path = ProjectSettings::get_singleton()->globalize_path(path); - OS::get_singleton()->shell_open(String("file://") + path); +void FileSystemDock::_file_option(int p_option, const Vector<String> p_selected) { + // The first one should be the active item + + switch (p_option) { + case FILE_SHOW_IN_EXPLORER: { + // Show the file / folder in the OS explorer + String fpath = path; + if (!fpath.ends_with("/")) { + fpath = fpath.get_base_dir(); + } + String dir = ProjectSettings::get_singleton()->globalize_path(fpath); + OS::get_singleton()->shell_open(String("file://") + dir); } break; + case FILE_OPEN: { - for (int i = 0; i < files->get_item_count(); i++) { - if (files->is_selected(i)) { - _select_file(i); - } + // Open the file + for (int i = 0; i < p_selected.size(); i++) { + _select_file(p_selected[i]); } } break; - case FILE_INSTANCE: { + case FILE_INSTANCE: { + // Instance all selected scenes Vector<String> paths; - - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) - continue; - String fpath = files->get_item_metadata(i); + for (int i = 0; i < p_selected.size(); i++) { + String fpath = p_selected[i]; if (EditorFileSystem::get_singleton()->get_file_type(fpath) == "PackedScene") { paths.push_back(fpath); } } - if (!paths.empty()) { emit_signal("instance", paths); } } break; - case FILE_DEPENDENCIES: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - String fpath = files->get_item_metadata(idx); - deps_editor->edit(fpath); + case FILE_ADD_FAVORITE: { + // Add the files from favorites + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < p_selected.size(); i++) { + if (favorites.find(p_selected[i]) == -1) { + favorites.push_back(p_selected[i]); + } + } + EditorSettings::get_singleton()->set_favorites(favorites); + _update_tree(_compute_uncollapsed_paths()); } break; - case FILE_OWNERS: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - String fpath = files->get_item_metadata(idx); - owners_editor->show(fpath); + case FILE_REMOVE_FAVORITE: { + // Remove the files from favorites + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < p_selected.size(); i++) { + favorites.erase(p_selected[i]); + } + EditorSettings::get_singleton()->set_favorites(favorites); + _update_tree(_compute_uncollapsed_paths()); + } break; + + case FILE_DEPENDENCIES: { + // Checkout the file dependencies + if (!p_selected.empty()) { + String fpath = p_selected[0]; + deps_editor->edit(fpath); + } } break; + + case FILE_OWNERS: { + // Checkout the file owners + if (!p_selected.empty()) { + String fpath = p_selected[0]; + owners_editor->show(fpath); + } + } break; + case FILE_MOVE: { + // Move the files to a given location to_move.clear(); - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) - continue; - - String fpath = files->get_item_metadata(i); - to_move.push_back(FileOrFolder(fpath, !fpath.ends_with("/"))); + for (int i = 0; i < p_selected.size(); i++) { + String fpath = p_selected[i]; + if (fpath != "res://") { + to_move.push_back(FileOrFolder(fpath, !fpath.ends_with("/"))); + } } if (to_move.size() > 0) { move_dialog->popup_centered_ratio(); } } break; - case FILE_RENAME: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - to_rename.path = files->get_item_metadata(idx); - to_rename.is_file = !to_rename.path.ends_with("/"); - if (to_rename.is_file) { - String name = to_rename.path.get_file(); - rename_dialog->set_title(TTR("Renaming file:") + " " + name); - rename_dialog_text->set_text(name); - rename_dialog_text->select(0, name.find_last(".")); - } else { - String name = to_rename.path.substr(0, to_rename.path.length() - 1).get_file(); - rename_dialog->set_title(TTR("Renaming folder:") + " " + name); - rename_dialog_text->set_text(name); - rename_dialog_text->select(0, name.length()); + case FILE_RENAME: { + // Rename the active file + if (!p_selected.empty()) { + to_rename.path = p_selected[0]; + if (to_rename.path != "res://") { + to_rename.is_file = !to_rename.path.ends_with("/"); + if (to_rename.is_file) { + String name = to_rename.path.get_file(); + rename_dialog->set_title(TTR("Renaming file:") + " " + name); + rename_dialog_text->set_text(name); + rename_dialog_text->select(0, name.find_last(".")); + } else { + String name = to_rename.path.substr(0, to_rename.path.length() - 1).get_file(); + rename_dialog->set_title(TTR("Renaming folder:") + " " + name); + rename_dialog_text->set_text(name); + rename_dialog_text->select(0, name.length()); + } + rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); + rename_dialog_text->grab_focus(); + } } - rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - rename_dialog_text->grab_focus(); } break; + case FILE_REMOVE: { + // Remove the selected files Vector<String> remove_files; Vector<String> remove_folders; - for (int i = 0; i < files->get_item_count(); i++) { - String fpath = files->get_item_metadata(i); - if (files->is_selected(i) && fpath != "res://") { + for (int i = 0; i < p_selected.size(); i++) { + String fpath = p_selected[i]; + if (fpath != "res://") { if (fpath.ends_with("/")) { remove_folders.push_back(fpath); } else { @@ -1259,166 +1483,77 @@ void FileSystemDock::_file_option(int p_option) { if (remove_files.size() + remove_folders.size() > 0) { remove_dialog->show(remove_folders, remove_files); - //1) find if used - //2) warn } } break; - case FILE_DUPLICATE: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - to_duplicate.path = files->get_item_metadata(idx); - to_duplicate.is_file = !to_duplicate.path.ends_with("/"); - if (to_duplicate.is_file) { - String name = to_duplicate.path.get_file(); - duplicate_dialog->set_title(TTR("Duplicating file:") + " " + name); - duplicate_dialog_text->set_text(name); - duplicate_dialog_text->select(0, name.find_last(".")); - } else { - String name = to_duplicate.path.substr(0, to_duplicate.path.length() - 1).get_file(); - duplicate_dialog->set_title(TTR("Duplicating folder:") + " " + name); - duplicate_dialog_text->set_text(name); - duplicate_dialog_text->select(0, name.length()); + case FILE_DUPLICATE: { + // Duplicate the selected files + for (int i = 0; i < p_selected.size(); i++) { + to_duplicate.path = p_selected[i]; + to_duplicate.is_file = !to_duplicate.path.ends_with("/"); + if (to_duplicate.is_file) { + String name = to_duplicate.path.get_file(); + duplicate_dialog->set_title(TTR("Duplicating file:") + " " + name); + duplicate_dialog_text->set_text(name); + duplicate_dialog_text->select(0, name.find_last(".")); + } else { + String name = to_duplicate.path.substr(0, to_duplicate.path.length() - 1).get_file(); + duplicate_dialog->set_title(TTR("Duplicating folder:") + " " + name); + duplicate_dialog_text->set_text(name); + duplicate_dialog_text->select(0, name.length()); + } + duplicate_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); + duplicate_dialog_text->grab_focus(); } - duplicate_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - duplicate_dialog_text->grab_focus(); } break; + case FILE_INFO: { } break; - case FILE_REIMPORT: { + case FILE_REIMPORT: { + // Reimport all selected files Vector<String> reimport; - for (int i = 0; i < files->get_item_count(); i++) { - - if (!files->is_selected(i)) - continue; - - String fpath = files->get_item_metadata(i); - reimport.push_back(fpath); + for (int i = 0; i < p_selected.size(); i++) { + reimport.push_back(p_selected[i]); } ERR_FAIL_COND(reimport.size() == 0); - /* - Ref<ResourceImportMetadata> rimd = ResourceLoader::load_import_metadata(reimport[0]); - ERR_FAIL_COND(!rimd.is_valid()); - String editor=rimd->get_editor(); - - if (editor.begins_with("texture_")) { //compatibility fix for old texture format - editor="texture"; - } - - Ref<EditorImportPlugin> rimp = EditorImportExport::get_singleton()->get_import_plugin_by_name(editor); - ERR_FAIL_COND(!rimp.is_valid()); - - if (reimport.size()==1) { - rimp->import_dialog(reimport[0]); - } else { - rimp->reimport_multiple_files(reimport); - - } - */ } break; + case FILE_NEW_FOLDER: { + // Create a new folder make_dir_dialog_text->set_text("new folder"); make_dir_dialog_text->select_all(); make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); make_dir_dialog_text->grab_focus(); } break; + case FILE_NEW_SCRIPT: { - String tarDir = path; - if (tarDir != "res://" && !tarDir.ends_with("/")) { - tarDir += "/"; + // Create a new script + String fpath = path; + if (!fpath.ends_with("/")) { + fpath = fpath.get_base_dir(); } - - make_script_dialog_text->config("Node", tarDir + "new_script.gd"); + make_script_dialog_text->config("Node", fpath + "new_script.gd"); make_script_dialog_text->popup_centered(Size2(300, 300) * EDSCALE); } break; + case FILE_COPY_PATH: { - int idx = files->get_current(); - if (idx < 0 || idx >= files->get_item_count()) - break; - String fpath = files->get_item_metadata(idx); - OS::get_singleton()->set_clipboard(fpath); + // Copy the file path + if (!p_selected.empty()) { + String fpath = p_selected[0]; + OS::get_singleton()->set_clipboard(fpath); + } } break; + case FILE_NEW_RESOURCE: { + // Create a new resource new_resource_dialog->popup_create(true); } break; } } -void FileSystemDock::_folder_option(int p_option) { - - TreeItem *selected = tree->get_selected(); - - switch (p_option) { - case FOLDER_EXPAND_ALL: - case FOLDER_COLLAPSE_ALL: { - bool is_collapsed = (p_option == FOLDER_COLLAPSE_ALL); - Vector<TreeItem *> needs_check; - needs_check.push_back(selected); - - while (needs_check.size()) { - needs_check[0]->set_collapsed(is_collapsed); - - TreeItem *child = needs_check[0]->get_children(); - while (child) { - needs_check.push_back(child); - child = child->get_next(); - } - - needs_check.remove(0); - } - } break; - case FOLDER_MOVE: { - to_move.clear(); - String fpath = selected->get_metadata(tree->get_selected_column()); - if (fpath != "res://") { - fpath = fpath.ends_with("/") ? fpath.substr(0, fpath.length() - 1) : fpath; - to_move.push_back(FileOrFolder(fpath, false)); - move_dialog->popup_centered_ratio(); - } - } break; - case FOLDER_RENAME: { - to_rename.path = selected->get_metadata(tree->get_selected_column()); - to_rename.is_file = false; - if (to_rename.path != "res://") { - String name = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1).get_file() : to_rename.path.get_file(); - rename_dialog->set_title(TTR("Renaming folder:") + " " + name); - rename_dialog_text->set_text(name); - rename_dialog_text->select(0, name.length()); - rename_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - rename_dialog_text->grab_focus(); - } - } break; - case FOLDER_REMOVE: { - Vector<String> remove_folders; - Vector<String> remove_files; - String fpath = selected->get_metadata(tree->get_selected_column()); - if (fpath != "res://") { - remove_folders.push_back(fpath); - remove_dialog->show(remove_folders, remove_files); - } - } break; - case FOLDER_NEW_FOLDER: { - make_dir_dialog_text->set_text("new folder"); - make_dir_dialog_text->select_all(); - make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE); - make_dir_dialog_text->grab_focus(); - } break; - case FOLDER_COPY_PATH: { - String fpath = selected->get_metadata(tree->get_selected_column()); - OS::get_singleton()->set_clipboard(fpath); - } break; - case FOLDER_SHOW_IN_EXPLORER: { - String fpath = selected->get_metadata(tree->get_selected_column()); - String dir = ProjectSettings::get_singleton()->globalize_path(fpath); - OS::get_singleton()->shell_open(String("file://") + dir); - } break; - } -} - void FileSystemDock::_resource_created() const { Object *c = new_resource_dialog->instance_selected(); @@ -1431,38 +1566,39 @@ void FileSystemDock::_resource_created() const { RES current_res = RES(r); - editor->save_resource_as(current_res, path); -} - -void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) { - folder_options->clear(); - folder_options->set_size(Size2(1, 1)); + String fpath = path; + if (!fpath.ends_with("/")) { + fpath = fpath.get_base_dir(); + } - folder_options->add_item(TTR("Expand all"), FOLDER_EXPAND_ALL); - folder_options->add_item(TTR("Collapse all"), FOLDER_COLLAPSE_ALL); + editor->save_resource_as(current_res, fpath); +} - TreeItem *item = tree->get_selected(); - if (item) { - String fpath = item->get_metadata(tree->get_selected_column()); - folder_options->add_separator(); - folder_options->add_item(TTR("Copy Path"), FOLDER_COPY_PATH); - if (fpath != "res://") { - folder_options->add_item(TTR("Rename..."), FOLDER_RENAME); - folder_options->add_item(TTR("Move To..."), FOLDER_MOVE); - folder_options->add_item(TTR("Delete"), FOLDER_REMOVE); - } - folder_options->add_separator(); - folder_options->add_item(TTR("New Folder..."), FOLDER_NEW_FOLDER); - folder_options->add_item(TTR("Open In File Manager"), FOLDER_SHOW_IN_EXPLORER); +void FileSystemDock::_search_changed(const String &p_text, const Control *p_from) { + if (searched_string.length() == 0 && p_text.length() > 0) { + // Register the uncollapsed paths before they change + uncollapsed_paths_before_search = _compute_uncollapsed_paths(); } - folder_options->set_position(tree->get_global_position() + p_pos); - folder_options->popup(); -} -void FileSystemDock::_search_changed(const String &p_text) { + searched_string = p_text.to_lower(); - if (file_list_vb->is_visible()) - _update_files(false); + if (p_from == tree_search_box) + file_list_search_box->set_text(searched_string); + else // file_list_search_box + tree_search_box->set_text(searched_string); + + switch (display_mode) { + case DISPLAY_MODE_FILE_LIST_ONLY: { + _update_files(false); + } break; + case DISPLAY_MODE_TREE_ONLY: { + _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); + } break; + case DISPLAY_MODE_SPLIT: { + _update_files(false); + _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); + } break; + } } void FileSystemDock::_rescan() { @@ -1471,20 +1607,25 @@ void FileSystemDock::_rescan() { EditorFileSystem::get_singleton()->scan(); } +void FileSystemDock::_toggle_split_mode(bool p_active) { + display_mode_setting = p_active ? DISPLAY_MODE_SETTING_SPLIT : DISPLAY_MODE_SETTING_TREE_ONLY; + EditorSettings::get_singleton()->set("docks/filesystem/display_mode", int(display_mode_setting)); + _update_display_mode(); +} + void FileSystemDock::fix_dependencies(const String &p_for_file) { deps_editor->edit(p_for_file); } void FileSystemDock::focus_on_filter() { - if (display_mode == DISPLAY_FILE_LIST_ONLY && tree->is_visible()) { + if (display_mode == DISPLAY_MODE_FILE_LIST_ONLY && tree->is_visible()) { // Tree mode, switch to files list with search box tree->hide(); file_list_vb->show(); - button_favorite->hide(); } - search_box->grab_focus(); + file_list_search_box->grab_focus(); } void FileSystemDock::set_file_list_display_mode(int p_mode) { @@ -1497,33 +1638,43 @@ void FileSystemDock::set_file_list_display_mode(int p_mode) { } Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) { - bool is_favorite = false; + bool all_favorites = true; + bool all_not_favorites = true; + Vector<String> paths; if (p_from == tree) { - TreeItem *selected = tree->get_selected(); - if (!selected) - return Variant(); - - String folder = selected->get_metadata(0); - if (folder == String()) - return Variant(); - - paths.push_back(folder.ends_with("/") ? folder : (folder + "/")); - is_favorite = selected->get_parent() != NULL && tree->get_root()->get_children() == selected->get_parent(); + // Check if the first selected is in favorite + TreeItem *selected = tree->get_next_selected(tree->get_root()); + while (selected) { + bool is_favorite = selected->get_parent() != NULL && tree->get_root()->get_children() == selected->get_parent(); + all_favorites &= is_favorite; + all_not_favorites &= !is_favorite; + selected = tree->get_next_selected(selected); + } + if (all_favorites) { + paths = _tree_get_selected(false); + } else { + paths = _tree_get_selected(); + } } else if (p_from == files) { for (int i = 0; i < files->get_item_count(); i++) { if (files->is_selected(i)) { paths.push_back(files->get_item_metadata(i)); } } + all_favorites = false; + all_not_favorites = true; } if (paths.empty()) return Variant(); + if (!all_favorites && !all_not_favorites) + return Variant(); + Dictionary drag_data = EditorNode::get_singleton()->drag_files_and_dirs(paths, p_from); - if (is_favorite) { + if (all_favorites) { drag_data["type"] = "favorite"; } return drag_data; @@ -1535,34 +1686,46 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { - //moving favorite around + // Moving favorite around TreeItem *ti = tree->get_item_at_position(p_point); if (!ti) return false; - int what = tree->get_drop_section_at_position(p_point); + int drop_section = tree->get_drop_section_at_position(p_point); + TreeItem *favorites_item = tree->get_root()->get_children(); + + TreeItem *resources_item = favorites_item->get_next(); - if (ti == tree->get_root()->get_children()) { - return (what == 1); //the parent, first fav + if (ti == favorites_item) { + return (drop_section == 1); // The parent, first fav } - if (ti->get_parent() && tree->get_root()->get_children() == ti->get_parent()) { - return true; // a favorite + if (ti->get_parent() && favorites_item == ti->get_parent()) { + return true; // A favorite } - - if (ti == tree->get_root()->get_children()->get_next()) { - return (what == -1); //the tree, last fav + if (ti == resources_item) { + return (drop_section == -1); // The tree, last fav } return false; } if (drag_data.has("type") && String(drag_data["type"]) == "resource") { - String to_dir = _get_drag_target_folder(p_point, p_from); + // Move resources + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); return !to_dir.empty(); } if (drag_data.has("type") && (String(drag_data["type"]) == "files" || String(drag_data["type"]) == "files_and_dirs")) { - String to_dir = _get_drag_target_folder(p_point, p_from); + // Move files or dir + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); + + if (favorite) + return true; + if (to_dir.empty()) return false; @@ -1587,71 +1750,67 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, return; Dictionary drag_data = p_data; - if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { + Vector<String> dirs = EditorSettings::get_singleton()->get_favorites(); - //moving favorite around + if (drag_data.has("type") && String(drag_data["type"]) == "favorite") { + // Moving favorite around TreeItem *ti = tree->get_item_at_position(p_point); if (!ti) return; + int drop_section = tree->get_drop_section_at_position(p_point); + int drop_position; Vector<String> files = drag_data["files"]; - - ERR_FAIL_COND(files.size() != 1); - - String swap = files[0]; - if (swap != "res://" && swap.ends_with("/")) { - swap = swap.substr(0, swap.length() - 1); - } - - int what = tree->get_drop_section_at_position(p_point); - - TreeItem *swap_item = NULL; - - if (ti == tree->get_root()->get_children()) { - swap_item = tree->get_root()->get_children()->get_children(); - - } else if (ti->get_parent() && tree->get_root()->get_children() == ti->get_parent()) { - if (what == -1) { - swap_item = ti; - } else { - swap_item = ti->get_next(); + TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *resources_item = favorites_item->get_next(); + + if (ti == favorites_item) { + // Drop on the favorite folder + drop_position = 0; + } else if (ti == resources_item) { + // Drop on the resouce item + drop_position = dirs.size(); + } else { + // Drop in the list + drop_position = dirs.find(ti->get_metadata(0)); + if (drop_section == 1) { + drop_position++; } } - String swap_with; - - if (swap_item) { - swap_with = swap_item->get_metadata(0); - if (swap_with != "res://" && swap_with.ends_with("/")) { - swap_with = swap_with.substr(0, swap_with.length() - 1); + // Remove dragged favorites + Vector<int> to_remove; + int offset = 0; + for (int i = 0; i < files.size(); i++) { + int to_remove_pos = dirs.find(files[i]); + to_remove.push_back(to_remove_pos); + if (to_remove_pos < drop_position) { + offset++; } } + drop_position -= offset; + to_remove.sort(); + for (int i = 0; i < to_remove.size(); i++) { + dirs.remove(to_remove[i] - i); + } - if (swap == swap_with) - return; - - Vector<String> dirs = EditorSettings::get_singleton()->get_favorite_dirs(); - - ERR_FAIL_COND(dirs.find(swap) == -1); - ERR_FAIL_COND(swap_with != String() && dirs.find(swap_with) == -1); - - dirs.erase(swap); - - if (swap_with == String()) { - dirs.push_back(swap); - } else { - int idx = dirs.find(swap_with); - dirs.insert(idx, swap); + // Re-add them at the right position + for (int i = 0; i < files.size(); i++) { + dirs.insert(drop_position, files[i]); + drop_position++; } - EditorSettings::get_singleton()->set_favorite_dirs(dirs); - _update_tree(true); + EditorSettings::get_singleton()->set_favorites(dirs); + _update_tree(_compute_uncollapsed_paths()); return; } if (drag_data.has("type") && String(drag_data["type"]) == "resource") { + // Moving resource Ref<Resource> res = drag_data["resource"]; - String to_dir = _get_drag_target_folder(p_point, p_from); + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); if (res.is_valid() && !to_dir.empty()) { EditorNode::get_singleton()->push_item(res.ptr()); EditorNode::get_singleton()->save_resource_as(res, to_dir); @@ -1659,7 +1818,10 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, } if (drag_data.has("type") && (String(drag_data["type"]) == "files" || String(drag_data["type"]) == "files_and_dirs")) { - String to_dir = _get_drag_target_folder(p_point, p_from); + // Move files or add to favorites + String to_dir; + bool favorite; + _get_drag_target_folder(to_dir, favorite, p_point, p_from); if (!to_dir.empty()) { Vector<String> fnames = drag_data["files"]; to_move.clear(); @@ -1667,50 +1829,93 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, to_move.push_back(FileOrFolder(fnames[i], !fnames[i].ends_with("/"))); } _move_operation_confirm(to_dir); + } else if (favorite) { + // Add the files from favorites + Vector<String> fnames = drag_data["files"]; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < fnames.size(); i++) { + if (favorites.find(fnames[i]) == -1) { + favorites.push_back(fnames[i]); + } + } + EditorSettings::get_singleton()->set_favorites(favorites); + _update_tree(_compute_uncollapsed_paths()); } } } -String FileSystemDock::_get_drag_target_folder(const Point2 &p_point, Control *p_from) const { +void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const { + target = String(); + target_favorites = false; + + // In the file list if (p_from == files) { int pos = files->get_item_at_position(p_point, true); - if (pos == -1) - return path; + if (pos == -1) { + return; + } - String target = files->get_item_metadata(pos); - return target.ends_with("/") ? target : path; + String ltarget = files->get_item_metadata(pos); + target = ltarget.ends_with("/") ? target : path; + return; } + // In the tree if (p_from == tree) { TreeItem *ti = tree->get_item_at_position(p_point); - if (ti && ti != tree->get_root()->get_children()) - return ti->get_metadata(0); + int section = tree->get_drop_section_at_position(p_point); + if (ti) { + // Check the favorites first + if (ti == tree->get_root()->get_children() && section >= 0) { + target_favorites = true; + return; + } else if (ti->get_parent() == tree->get_root()->get_children()) { + target_favorites = true; + return; + } else { + String fpath = ti->get_metadata(0); + if (section == 0) { + if (fpath.ends_with("/")) { + // We drop on a folder + target = fpath; + return; + } + } else { + if (ti->get_parent() != tree->get_root()->get_children()) { + // Not in the favorite section + if (fpath != "res://") { + // We drop between two files + if (fpath.ends_with("/")) { + fpath = fpath.substr(0, fpath.length() - 1); + } + target = fpath.get_base_dir(); + return; + } + } + } + } + } } - return String(); + return; } -void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) { - - //Right clicking ".." should clear current selection - if (files->get_item_text(p_item) == "..") { - for (int i = 0; i < files->get_item_count(); i++) { - files->unselect(i); - } - } +void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths) { + // Add options for files and folders + ERR_FAIL_COND(p_paths.empty()) Vector<String> filenames; Vector<String> foldernames; + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + bool all_files = true; bool all_files_scenes = true; bool all_folders = true; - for (int i = 0; i < files->get_item_count(); i++) { - if (!files->is_selected(i)) { - continue; - } - - String fpath = files->get_item_metadata(i); + bool all_favorites = true; + bool all_not_favorites = true; + for (int i = 0; i < p_paths.size(); i++) { + String fpath = p_paths[i]; if (fpath.ends_with("/")) { foldernames.push_back(fpath); all_files = false; @@ -1719,68 +1924,135 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) { all_folders = false; all_files_scenes &= (EditorFileSystem::get_singleton()->get_file_type(fpath) == "PackedScene"); } + + // Check if in favorites + bool found = false; + for (int j = 0; j < favorites.size(); j++) { + if (favorites[j] == fpath) { + found = true; + break; + } + } + if (found) { + all_not_favorites = false; + } else { + all_favorites = false; + } } - file_options->clear(); - file_options->set_size(Size2(1, 1)); if (all_files) { if (all_files_scenes && filenames.size() >= 1) { - file_options->add_item(TTR("Open Scene(s)"), FILE_OPEN); - file_options->add_item(TTR("Instance"), FILE_INSTANCE); - file_options->add_separator(); + p_popup->add_item(TTR("Open Scene(s)"), FILE_OPEN); + p_popup->add_item(TTR("Instance"), FILE_INSTANCE); + p_popup->add_separator(); } if (!all_files_scenes && filenames.size() == 1) { - file_options->add_item(TTR("Open"), FILE_OPEN); - file_options->add_separator(); + p_popup->add_item(TTR("Open"), FILE_OPEN); + p_popup->add_separator(); + } + } + + if (p_paths.size() >= 1) { + if (!all_favorites) { + p_popup->add_item(TTR("Add to favorites"), FILE_ADD_FAVORITE); + } + if (!all_not_favorites) { + p_popup->add_item(TTR("Remove from favorites"), FILE_REMOVE_FAVORITE); } + p_popup->add_separator(); + } + if (all_files) { if (filenames.size() == 1) { - file_options->add_item(TTR("Edit Dependencies..."), FILE_DEPENDENCIES); - file_options->add_item(TTR("View Owners..."), FILE_OWNERS); - file_options->add_separator(); + p_popup->add_item(TTR("Edit Dependencies..."), FILE_DEPENDENCIES); + p_popup->add_item(TTR("View Owners..."), FILE_OWNERS); + p_popup->add_separator(); } } else if (all_folders && foldernames.size() > 0) { - file_options->add_item(TTR("Open"), FILE_OPEN); - file_options->add_separator(); + p_popup->add_item(TTR("Open"), FILE_OPEN); + p_popup->add_separator(); } - int num_items = filenames.size() + foldernames.size(); - if (num_items >= 1) { - if (num_items == 1) { - file_options->add_item(TTR("Copy Path"), FILE_COPY_PATH); - file_options->add_item(TTR("Rename..."), FILE_RENAME); - file_options->add_item(TTR("Duplicate..."), FILE_DUPLICATE); - } - file_options->add_item(TTR("Move To..."), FILE_MOVE); - file_options->add_item(TTR("Delete"), FILE_REMOVE); - file_options->add_separator(); + if (p_paths.size() == 1) { + p_popup->add_item(TTR("Copy Path"), FILE_COPY_PATH); + p_popup->add_item(TTR("Rename..."), FILE_RENAME); + p_popup->add_item(TTR("Duplicate..."), FILE_DUPLICATE); } - file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); - file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); - file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + p_popup->add_item(TTR("Move To..."), FILE_MOVE); + p_popup->add_item(TTR("Delete"), FILE_REMOVE); - String fpath = files->get_item_metadata(p_item); - String item_text = fpath.ends_with("/") ? TTR("Open In File Manager") : TTR("Show In File Manager"); - file_options->add_item(item_text, FILE_SHOW_IN_EXPLORER); + if (p_paths.size() == 1) { + p_popup->add_separator(); + p_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); + p_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + p_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); - file_options->set_position(files->get_global_position() + p_pos); - file_options->popup(); + String fpath = p_paths[0]; + String item_text = fpath.ends_with("/") ? TTR("Open In File Manager") : TTR("Show In File Manager"); + p_popup->add_item(item_text, FILE_SHOW_IN_EXPLORER); + } +} + +void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos) { + // Right click is pressed in the tree + Vector<String> paths = _tree_get_selected(); + + if (paths.size() == 1) { + if (paths[0].ends_with("/")) { + tree_popup->add_item(TTR("Expand all"), FOLDER_EXPAND_ALL); + tree_popup->add_item(TTR("Collapse all"), FOLDER_COLLAPSE_ALL); + tree_popup->add_separator(); + } + } + + // Popup + if (!paths.empty()) { + tree_popup->clear(); + tree_popup->set_size(Size2(1, 1)); + _file_and_folders_fill_popup(tree_popup, paths); + tree_popup->set_position(tree->get_global_position() + p_pos); + tree_popup->popup(); + } } -void FileSystemDock::_rmb_pressed(const Vector2 &p_pos) { - file_options->clear(); - file_options->set_size(Size2(1, 1)); +void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) { + // Right click is pressed in the file list + Vector<String> paths; + for (int i = 0; i < files->get_item_count(); i++) { + if (!files->is_selected(i)) + continue; + if (files->get_item_text(p_item) == "..") { + files->unselect(i); + continue; + } + paths.push_back(files->get_item_metadata(i)); + } + + // Popup + if (!paths.empty()) { + file_list_popup->clear(); + file_list_popup->set_size(Size2(1, 1)); + _file_and_folders_fill_popup(file_list_popup, paths); + file_list_popup->set_position(files->get_global_position() + p_pos); + file_list_popup->popup(); + } +} - file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); - file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); - file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); - file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); - file_options->set_position(files->get_global_position() + p_pos); - file_options->popup(); +void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) { + // Right click on empty space for file list + file_list_popup->clear(); + file_list_popup->set_size(Size2(1, 1)); + + file_list_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); + file_list_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + file_list_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + file_list_popup->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); + file_list_popup->set_position(files->get_global_position() + p_pos); + file_list_popup->popup(); } void FileSystemDock::select_file(const String &p_file) { @@ -1790,11 +2062,24 @@ void FileSystemDock::select_file(const String &p_file) { void FileSystemDock::_file_multi_selected(int p_index, bool p_selected) { + // Set the path to the current focussed item + int current = files->get_current(); + if (current == p_index) { + String fpath = files->get_item_metadata(current); + if (!fpath.ends_with("/")) { + path = fpath; + if (display_mode == DISPLAY_MODE_SPLIT) { + _update_tree(_compute_uncollapsed_paths()); + } + } + } + + // Update the import dock import_dock_needs_update = true; call_deferred("_update_import_dock"); } -void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) { +void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { if (get_viewport()->get_modal_stack_top()) return; //ignore because of modal window @@ -1802,21 +2087,34 @@ void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) { Ref<InputEventKey> key = p_event; if (key.is_valid() && key->is_pressed() && !key->is_echo()) { if (ED_IS_SHORTCUT("filesystem_dock/duplicate", p_event)) { - _file_option(FILE_DUPLICATE); + _tree_rmb_option(FILE_DUPLICATE); } else if (ED_IS_SHORTCUT("filesystem_dock/copy_path", p_event)) { - _file_option(FILE_COPY_PATH); + _tree_rmb_option(FILE_COPY_PATH); } else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) { - _file_option(FILE_REMOVE); + _tree_rmb_option(FILE_REMOVE); } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) { - _file_option(FILE_RENAME); + _tree_rmb_option(FILE_RENAME); } } } -void FileSystemDock::_file_selected() { +void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { - import_dock_needs_update = true; - _update_import_dock(); + if (get_viewport()->get_modal_stack_top()) + return; //ignore because of modal window + + Ref<InputEventKey> key = p_event; + if (key.is_valid() && key->is_pressed() && !key->is_echo()) { + if (ED_IS_SHORTCUT("filesystem_dock/duplicate", p_event)) { + _file_list_rmb_option(FILE_DUPLICATE); + } else if (ED_IS_SHORTCUT("filesystem_dock/copy_path", p_event)) { + _file_list_rmb_option(FILE_COPY_PATH); + } else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) { + _file_list_rmb_option(FILE_REMOVE); + } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) { + _file_list_rmb_option(FILE_RENAME); + } + } } void FileSystemDock::_update_import_dock() { @@ -1869,16 +2167,25 @@ void FileSystemDock::_update_import_dock() { void FileSystemDock::_bind_methods() { - ClassDB::bind_method(D_METHOD("_files_gui_input"), &FileSystemDock::_files_gui_input); + ClassDB::bind_method(D_METHOD("_file_list_gui_input"), &FileSystemDock::_file_list_gui_input); + ClassDB::bind_method(D_METHOD("_tree_gui_input"), &FileSystemDock::_tree_gui_input); + ClassDB::bind_method(D_METHOD("_update_tree"), &FileSystemDock::_update_tree); ClassDB::bind_method(D_METHOD("_rescan"), &FileSystemDock::_rescan); - ClassDB::bind_method(D_METHOD("_favorites_pressed"), &FileSystemDock::_favorites_pressed); - ClassDB::bind_method(D_METHOD("_show_current_scene_file"), &FileSystemDock::_show_current_scene_file); - //ClassDB::bind_method(D_METHOD("_instance_pressed"),&ScenesDock::_instance_pressed); - ClassDB::bind_method(D_METHOD("_go_to_file_list"), &FileSystemDock::_go_to_file_list); - ClassDB::bind_method(D_METHOD("_dir_rmb_pressed"), &FileSystemDock::_dir_rmb_pressed); - ClassDB::bind_method(D_METHOD("_thumbnail_done"), &FileSystemDock::_thumbnail_done); + ClassDB::bind_method(D_METHOD("_toggle_split_mode"), &FileSystemDock::_toggle_split_mode); + + ClassDB::bind_method(D_METHOD("_tree_rmb_option", "option"), &FileSystemDock::_tree_rmb_option); + ClassDB::bind_method(D_METHOD("_file_list_rmb_option", "option"), &FileSystemDock::_file_list_rmb_option); + + ClassDB::bind_method(D_METHOD("_tree_rmb_select"), &FileSystemDock::_tree_rmb_select); + ClassDB::bind_method(D_METHOD("_file_list_rmb_select"), &FileSystemDock::_file_list_rmb_select); + ClassDB::bind_method(D_METHOD("_file_list_rmb_pressed"), &FileSystemDock::_file_list_rmb_pressed); + + ClassDB::bind_method(D_METHOD("_file_list_thumbnail_done"), &FileSystemDock::_file_list_thumbnail_done); + ClassDB::bind_method(D_METHOD("_tree_thumbnail_done"), &FileSystemDock::_tree_thumbnail_done); + ClassDB::bind_method(D_METHOD("_file_list_activate_file"), &FileSystemDock::_file_list_activate_file); + ClassDB::bind_method(D_METHOD("_tree_activate_file"), &FileSystemDock::_tree_activate_file); ClassDB::bind_method(D_METHOD("_select_file"), &FileSystemDock::_select_file); ClassDB::bind_method(D_METHOD("_go_to_tree"), &FileSystemDock::_go_to_tree); ClassDB::bind_method(D_METHOD("navigate_to_path"), &FileSystemDock::navigate_to_path); @@ -1886,9 +2193,7 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_fw_history"), &FileSystemDock::_fw_history); ClassDB::bind_method(D_METHOD("_bw_history"), &FileSystemDock::_bw_history); ClassDB::bind_method(D_METHOD("_fs_changed"), &FileSystemDock::_fs_changed); - ClassDB::bind_method(D_METHOD("_dir_selected"), &FileSystemDock::_dir_selected); - ClassDB::bind_method(D_METHOD("_file_option"), &FileSystemDock::_file_option); - ClassDB::bind_method(D_METHOD("_folder_option"), &FileSystemDock::_folder_option); + ClassDB::bind_method(D_METHOD("_tree_multi_selected"), &FileSystemDock::_tree_multi_selected); ClassDB::bind_method(D_METHOD("_make_dir_confirm"), &FileSystemDock::_make_dir_confirm); ClassDB::bind_method(D_METHOD("_resource_created"), &FileSystemDock::_resource_created); ClassDB::bind_method(D_METHOD("_move_operation_confirm", "to_path", "overwrite"), &FileSystemDock::_move_operation_confirm, DEFVAL(false)); @@ -1901,13 +2206,10 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &FileSystemDock::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &FileSystemDock::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw"), &FileSystemDock::drop_data_fw); - ClassDB::bind_method(D_METHOD("_files_list_rmb_select"), &FileSystemDock::_files_list_rmb_select); ClassDB::bind_method(D_METHOD("_preview_invalidated"), &FileSystemDock::_preview_invalidated); - ClassDB::bind_method(D_METHOD("_file_selected"), &FileSystemDock::_file_selected); ClassDB::bind_method(D_METHOD("_file_multi_selected"), &FileSystemDock::_file_multi_selected); ClassDB::bind_method(D_METHOD("_update_import_dock"), &FileSystemDock::_update_import_dock); - ClassDB::bind_method(D_METHOD("_rmb_pressed"), &FileSystemDock::_rmb_pressed); ADD_SIGNAL(MethodInfo("instance", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"))); ADD_SIGNAL(MethodInfo("open")); @@ -1924,9 +2226,12 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), KEY_DELETE); ED_SHORTCUT("filesystem_dock/rename", TTR("Rename")); + VBoxContainer *top_vbc = memnew(VBoxContainer); + add_child(top_vbc); + HBoxContainer *toolbar_hbc = memnew(HBoxContainer); toolbar_hbc->add_constant_override("separation", 0); - add_child(toolbar_hbc); + top_vbc->add_child(toolbar_hbc); button_hist_prev = memnew(ToolButton); button_hist_prev->set_disabled(true); @@ -1942,6 +2247,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { current_path = memnew(LineEdit); current_path->set_h_size_flags(SIZE_EXPAND_FILL); + current_path->set_text(path); toolbar_hbc->add_child(current_path); button_reload = memnew(Button); @@ -1952,22 +2258,25 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_reload->hide(); toolbar_hbc->add_child(button_reload); - //toolbar_hbc->add_spacer(); + button_toggle_display_mode = memnew(Button); + button_toggle_display_mode->set_flat(true); + button_toggle_display_mode->set_toggle_mode(true); + button_toggle_display_mode->connect("toggled", this, "_toggle_split_mode"); + button_toggle_display_mode->set_focus_mode(FOCUS_NONE); + button_toggle_display_mode->set_tooltip(TTR("Toggle split mode")); + toolbar_hbc->add_child(button_toggle_display_mode); - button_favorite = memnew(Button); - button_favorite->set_flat(true); - button_favorite->set_toggle_mode(true); - button_favorite->connect("pressed", this, "_favorites_pressed"); - button_favorite->set_tooltip(TTR("Toggle folder status as Favorite.")); - button_favorite->set_focus_mode(FOCUS_NONE); - toolbar_hbc->add_child(button_favorite); - - button_show = memnew(Button); - button_show->set_flat(true); - button_show->connect("pressed", this, "_show_current_scene_file"); - toolbar_hbc->add_child(button_show); - button_show->set_focus_mode(FOCUS_NONE); - button_show->set_tooltip(TTR("Show current scene file.")); + HBoxContainer *toolbar2_hbc = memnew(HBoxContainer); + toolbar2_hbc->add_constant_override("separation", 0); + top_vbc->add_child(toolbar2_hbc); + + tree_search_box = memnew(LineEdit); + tree_search_box->set_h_size_flags(SIZE_EXPAND_FILL); + tree_search_box->set_placeholder(TTR("Search files")); + tree_search_box->connect("text_changed", this, "_search_changed", varray(tree_search_box)); + toolbar2_hbc->add_child(tree_search_box); + + //toolbar_hbc->add_spacer(); //Control *spacer = memnew( Control); @@ -1990,13 +2299,13 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_instance->set_tooltip(TTR("Instance the selected scene(s) as child of the selected node.")); */ - file_options = memnew(PopupMenu); - file_options->set_hide_on_window_lose_focus(true); - add_child(file_options); + file_list_popup = memnew(PopupMenu); + file_list_popup->set_hide_on_window_lose_focus(true); + add_child(file_list_popup); - folder_options = memnew(PopupMenu); - folder_options->set_hide_on_window_lose_focus(true); - add_child(folder_options); + tree_popup = memnew(PopupMenu); + tree_popup->set_hide_on_window_lose_focus(true); + add_child(tree_popup); split_box = memnew(VSplitContainer); split_box->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2007,13 +2316,15 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { tree->set_hide_root(true); tree->set_drag_forwarding(this); tree->set_allow_rmb_select(true); + tree->set_select_mode(Tree::SELECT_MULTI); tree->set_custom_minimum_size(Size2(0, 200 * EDSCALE)); split_box->add_child(tree); tree->connect("item_edited", this, "_favorite_toggled"); - tree->connect("item_activated", this, "_go_to_file_list"); - tree->connect("cell_selected", this, "_dir_selected"); - tree->connect("item_rmb_selected", this, "_dir_rmb_pressed"); + tree->connect("item_activated", this, "_tree_activate_file"); + tree->connect("multi_selected", this, "_tree_multi_selected"); + tree->connect("item_rmb_selected", this, "_tree_rmb_select"); + tree->connect("gui_input", this, "_tree_gui_input"); file_list_vb = memnew(VBoxContainer); file_list_vb->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2027,11 +2338,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_tree->hide(); path_hb->add_child(button_tree); - search_box = memnew(LineEdit); - search_box->set_h_size_flags(SIZE_EXPAND_FILL); - search_box->set_placeholder(TTR("Search files")); - search_box->connect("text_changed", this, "_search_changed"); - path_hb->add_child(search_box); + file_list_search_box = memnew(LineEdit); + file_list_search_box->set_h_size_flags(SIZE_EXPAND_FILL); + file_list_search_box->set_placeholder(TTR("Search files")); + file_list_search_box->connect("text_changed", this, "_search_changed", varray(file_list_search_box)); + path_hb->add_child(file_list_search_box); button_file_list_display_mode = memnew(ToolButton); button_file_list_display_mode->set_toggle_mode(true); @@ -2041,11 +2352,10 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { files->set_v_size_flags(SIZE_EXPAND_FILL); files->set_select_mode(ItemList::SELECT_MULTI); files->set_drag_forwarding(this); - files->connect("item_rmb_selected", this, "_files_list_rmb_select"); - files->connect("gui_input", this, "_files_gui_input"); - files->connect("item_selected", this, "_file_selected"); + files->connect("item_rmb_selected", this, "_file_list_rmb_select"); + files->connect("gui_input", this, "_file_list_gui_input"); files->connect("multi_selected", this, "_file_multi_selected"); - files->connect("rmb_clicked", this, "_rmb_pressed"); + files->connect("rmb_clicked", this, "_file_list_rmb_pressed"); files->set_allow_rmb_select(true); file_list_vb->add_child(files); @@ -2123,7 +2433,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { new_resource_dialog->set_base_type("Resource"); new_resource_dialog->connect("create", this, "_resource_created"); + searched_string = String(); + uncollapsed_paths_before_search = Vector<String>(); + updating_tree = false; + tree_update_id = 0; initialized = false; import_dock_needs_update = false; @@ -2131,8 +2445,12 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { history_max_size = 20; history.push_back("res://"); - display_mode = DISPLAY_SPLIT; + display_mode = DISPLAY_MODE_SPLIT; file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS; + + display_mode_setting = DISPLAY_MODE_SETTING_TREE_ONLY; + old_display_mode_setting = DISPLAY_MODE_SETTING_TREE_ONLY; + always_show_folders = false; } FileSystemDock::~FileSystemDock() { diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 40be645bf7..51c8791b25 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -66,15 +66,22 @@ public: }; private: + enum DisplayModeSetting { + DISPLAY_MODE_SETTING_TREE_ONLY, + DISPLAY_MODE_SETTING_SPLIT, + }; + enum DisplayMode { - DISPLAY_TREE_ONLY, - DISPLAY_FILE_LIST_ONLY, - DISPLAY_SPLIT, + DISPLAY_MODE_TREE_ONLY, + DISPLAY_MODE_FILE_LIST_ONLY, + DISPLAY_MODE_SPLIT, }; enum FileMenu { FILE_OPEN, FILE_INSTANCE, + FILE_ADD_FAVORITE, + FILE_REMOVE_FAVORITE, FILE_DEPENDENCIES, FILE_OWNERS, FILE_MOVE, @@ -87,18 +94,9 @@ private: FILE_NEW_SCRIPT, FILE_SHOW_IN_EXPLORER, FILE_COPY_PATH, - FILE_NEW_RESOURCE - }; - - enum FolderMenu { + FILE_NEW_RESOURCE, FOLDER_EXPAND_ALL, FOLDER_COLLAPSE_ALL, - FOLDER_MOVE, - FOLDER_RENAME, - FOLDER_REMOVE, - FOLDER_NEW_FOLDER, - FOLDER_SHOW_IN_EXPLORER, - FOLDER_COPY_PATH }; VBoxContainer *scanning_vb; @@ -109,24 +107,30 @@ private: EditorNode *editor; Set<String> favorites; + Button *button_toggle_display_mode; Button *button_reload; - Button *button_favorite; Button *button_tree; Button *button_file_list_display_mode; Button *button_hist_next; Button *button_hist_prev; - Button *button_show; LineEdit *current_path; - LineEdit *search_box; + LineEdit *tree_search_box; + LineEdit *file_list_search_box; + + String searched_string; + Vector<String> uncollapsed_paths_before_search; + TextureRect *search_icon; HBoxContainer *path_hb; FileListDisplayMode file_list_display_mode; DisplayMode display_mode; + DisplayModeSetting display_mode_setting; + DisplayModeSetting old_display_mode_setting; bool file_list_view; - PopupMenu *file_options; - PopupMenu *folder_options; + PopupMenu *file_list_popup; + PopupMenu *tree_popup; DependencyEditor *deps_editor; DependencyEditorOwners *owners_editor; @@ -143,6 +147,8 @@ private: ScriptCreateDialog *make_script_dialog_text; CreateDialog *new_resource_dialog; + bool always_show_folders; + class FileOrFolder { public: String path; @@ -169,14 +175,18 @@ private: bool initialized; bool updating_tree; + int tree_update_id; Tree *tree; //directories ItemList *files; bool import_dock_needs_update; + Ref<Texture> _get_tree_item_icon(EditorFileSystemDirectory *p_dir, int p_idx); bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths); - void _update_tree(bool keep_collapse_state, bool p_uncollapse_root = false); + Vector<String> _compute_uncollapsed_paths(); + void _update_tree(const Vector<String> p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false); - void _files_gui_input(Ref<InputEvent> p_event); + void _file_list_gui_input(Ref<InputEvent> p_event); + void _tree_gui_input(Ref<InputEvent> p_event); void _update_files(bool p_keep_selection); void _update_file_list_display_mode_button(); @@ -186,12 +196,13 @@ private: void _go_to_tree(); void _go_to_file_list(); - void _select_file(int p_idx); + void _select_file(const String p_path); + void _tree_activate_file(); + void _file_list_activate_file(int p_idx); void _file_multi_selected(int p_index, bool p_selected); - void _update_import_dock(); + void _tree_multi_selected(Object *p_item, int p_column, bool p_selected); - void _file_selected(); - void _dir_selected(); + void _update_import_dock(); void _get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const; void _find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const; @@ -199,8 +210,8 @@ private: void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const; void _update_dependencies_after_move(const Map<String, String> &p_renames) const; void _update_resource_paths_after_move(const Map<String, String> &p_renames) const; - void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const; - void _update_project_settings_after_move(const Map<String, String> &p_renames) const; + void _update_favorites_list_after_move(const Map<String, String> &p_files_renames, const Map<String, String> &p_folders_renames) const; + void _update_project_settings_after_move(const Map<String, String> &p_folders_renames) const; void _resource_created() const; void _make_dir_confirm(); @@ -210,8 +221,9 @@ private: bool _check_existing(); void _move_operation_confirm(const String &p_to_path, bool overwrite = false); - void _file_option(int p_option); - void _folder_option(int p_option); + void _tree_rmb_option(int p_option); + void _file_list_rmb_option(int p_option); + void _file_option(int p_option, const Vector<String> p_selected); void _fw_history(); void _bw_history(); @@ -221,13 +233,14 @@ private: void _set_scanning_mode(); void _rescan(); - void _favorites_pressed(); - void _show_current_scene_file(); - void _search_changed(const String &p_text); + void _toggle_split_mode(bool p_active); - void _dir_rmb_pressed(const Vector2 &p_pos); - void _files_list_rmb_select(int p_item, const Vector2 &p_pos); - void _rmb_pressed(const Vector2 &p_pos); + void _search_changed(const String &p_text, const Control *p_from); + + void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths); + void _tree_rmb_select(const Vector2 &p_pos); + void _file_list_rmb_select(int p_item, const Vector2 &p_pos); + void _file_list_rmb_pressed(const Vector2 &p_pos); struct FileInfo { String name; @@ -238,7 +251,7 @@ private: bool import_broken; bool operator<(const FileInfo &fi) const { - return name < fi.name; + return NaturalNoCaseComparator()(name, fi.name); } }; @@ -247,13 +260,16 @@ private: Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); - String _get_drag_target_folder(const Point2 &p_point, Control *p_from) const; + void _get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const; void _preview_invalidated(const String &p_path); - void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _file_list_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); + void _tree_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); void _update_display_mode(); + Vector<String> _tree_get_selected(bool remove_self_inclusion = true); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/icons/icon_GUI_checked.svg b/editor/icons/icon_GUI_checked.svg index e5fa67ebf5..8d00eca8d3 100644 --- a/editor/icons/icon_GUI_checked.svg +++ b/editor/icons/icon_GUI_checked.svg @@ -1,3 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<path d="m4 2c-1.1046 0-2 0.89543-2 2v8c0 1.1046 0.89543 2 2 2h8c1.1046 0 2-0.89543 2-2v-8c0-1.1046-0.89543-2-2-2h-8zm7.293 2.293l1.4141 1.4141-6.707 6.707-2.707-2.707 1.4141-1.4141 1.293 1.293 5.293-5.293z" fill="#e0e0e0" fill-opacity=".78431"/> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill-opacity=".188235" stroke-width="1.166667"/><path d="m11.500773 3.7343508-5.6117507 5.6117502-1.7045017-1.6814543-1.4992276 1.4992276 3.2037293 3.1806817 7.1109777-7.1109775z" stroke-width="1.060227"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_radio_checked.svg b/editor/icons/icon_GUI_radio_checked.svg index 6a65d49eeb..447b57f8ae 100644 --- a/editor/icons/icon_GUI_radio_checked.svg +++ b/editor/icons/icon_GUI_radio_checked.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<circle cx="8" cy="1044.4" r="5" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".78431" stroke-width="2"/> -<circle cx="8" cy="1044.4" r="3" fill="#e0e0e0" fill-opacity=".78431"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill-opacity=".188235" stroke-width="2.333333"/><path d="m12 8a4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4 4 4 0 0 1 4 4" stroke-width="1.333333"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_radio_unchecked.svg b/editor/icons/icon_GUI_radio_unchecked.svg index 6e52a8af77..1e8117bd10 100644 --- a/editor/icons/icon_GUI_radio_unchecked.svg +++ b/editor/icons/icon_GUI_radio_unchecked.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<circle cx="8" cy="1044.4" r="5" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".78431" stroke-width="2"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m15 8a7 7 0 0 1 -7 7 7 7 0 0 1 -7-7 7 7 0 0 1 7-7 7 7 0 0 1 7 7" fill="#e0e0e0" fill-opacity=".188235" stroke-width="2.333333"/></svg>
\ No newline at end of file diff --git a/editor/icons/icon_GUI_unchecked.svg b/editor/icons/icon_GUI_unchecked.svg index 59df40954f..9575422df3 100644 --- a/editor/icons/icon_GUI_unchecked.svg +++ b/editor/icons/icon_GUI_unchecked.svg @@ -1,3 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg"> -<path d="m4 2a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2v-8a2 2 0 0 0 -2 -2h-8zm0.80078 2h6.3984a0.8 0.8 0 0 1 0.80078 0.80078v6.3984a0.8 0.8 0 0 1 -0.80078 0.80078h-6.3984a0.8 0.8 0 0 1 -0.80078 -0.80078v-6.3984a0.8 0.8 0 0 1 0.80078 -0.80078z" fill="#e0e0e0" fill-opacity=".78431"/> -</svg> +<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#e0e0e0" fill-opacity=".188235" stroke-width="1.166667"/></svg>
\ No newline at end of file diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index b18bbc8ce9..c237d2e854 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -126,7 +126,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT); String p = l.replace("map_Kd", "").replace("\\", "/").strip_edges(); - String path = base_path.plus_file(p); + String path; + if (p.is_abs_path()) { + path = p; + } else { + path = base_path.plus_file(p); + } Ref<Texture> texture = ResourceLoader::load(path); @@ -141,7 +146,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT); String p = l.replace("map_Ks", "").replace("\\", "/").strip_edges(); - String path = base_path.plus_file(p); + String path; + if (p.is_abs_path()) { + path = p; + } else { + path = base_path.plus_file(p); + } Ref<Texture> texture = ResourceLoader::load(path); @@ -156,7 +166,12 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Spati ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT); String p = l.replace("map_Ns", "").replace("\\", "/").strip_edges(); - String path = base_path.plus_file(p); + String path; + if (p.is_abs_path()) { + path = p; + } else { + path = base_path.plus_file(p); + } Ref<Texture> texture = ResourceLoader::load(path); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 5ab764fb15..81a798f0b6 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -104,6 +104,7 @@ void InspectorDock::_menu_option(int p_option) { res = duplicates[res]; current->set(E->get().name, res); + editor->get_inspector()->update_property(E->get().name); } } } @@ -158,7 +159,6 @@ void InspectorDock::_resource_file_selected(String p_file) { RES res = ResourceLoader::load(p_file); if (res.is_null()) { - warning_dialog->get_ok()->set_text(TTR("OK")); warning_dialog->set_text(TTR("Failed to load resource.")); return; }; @@ -319,7 +319,6 @@ void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Tran } void InspectorDock::_warning_pressed() { - warning_dialog->get_ok()->set_text(TTR("Ok")); warning_dialog->popup_centered_minsize(); } diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 2d341cdd93..f7e59e2beb 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -275,6 +275,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return (mb.is_valid() && mb->get_button_index() == 1); } + CanvasItemEditor::Tool tool = CanvasItemEditor::get_singleton()->get_current_tool(); + if (tool != CanvasItemEditor::TOOL_SELECT) + return false; + if (mb.is_valid()) { Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform(); @@ -283,10 +287,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position()))); if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) { - if (mb->get_button_index() == BUTTON_LEFT) { - if (mb->is_pressed()) { + if (mb->get_control() || mb->get_shift() || mb->get_alt()) + return false; const PosVertex insert = closest_edge_point(gpoint); @@ -512,12 +516,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return false; } -void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!_get_node()) return; - Control *vpc = canvas_item_editor->get_viewport_control(); - Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform(); const Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); @@ -558,7 +560,7 @@ void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 point = xform.xform(p); Vector2 next_point = xform.xform(p2); - vpc->draw_line(point, next_point, col, 2 * EDSCALE); + p_overlay->draw_line(point, next_point, col, 2 * EDSCALE); } } @@ -582,7 +584,7 @@ void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { p2 = points[(i + 1) % n_points] + offset; const Vector2 next_point = xform.xform(p2); - vpc->draw_line(point, next_point, col, 2 * EDSCALE); + p_overlay->draw_line(point, next_point, col, 2 * EDSCALE); } } @@ -594,14 +596,14 @@ void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) { const Vector2 point = xform.xform(p); const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); - vpc->draw_texture(handle, point - handle->get_size() * 0.5, modulate); + p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, modulate); } } if (edge_point.valid()) { Ref<Texture> add_handle = get_icon("EditorHandleAdd", "EditorIcons"); - vpc->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5); + p_overlay->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5); } } diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 9ba03bcdf6..00634ba5b8 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -137,7 +137,7 @@ protected: public: bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_polygon); AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wip_destructive = true); @@ -153,7 +153,7 @@ class AbstractPolygon2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return polygon_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { polygon_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { polygon_editor->forward_canvas_draw_over_viewport(p_overlay); } bool has_main_screen() const { return false; } virtual String get_name() const { return klass; } diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index bf7603bd86..445664f8dd 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -908,7 +908,7 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { } } -void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) { +void AnimationPlayerEditor::forward_canvas_force_draw_over_viewport(Control *p_overlay) { if (!onion.can_overlay) return; diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 5ac7b99903..55f082aadb 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -246,7 +246,7 @@ public: void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } void edit(AnimationPlayer *p_player); - void forward_force_draw_over_viewport(Control *p_overlay); + void forward_canvas_force_draw_over_viewport(Control *p_overlay); AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin); }; @@ -271,7 +271,7 @@ public: virtual bool handles(Object *p_object) const; virtual void make_visible(bool p_visible); - virtual void forward_force_draw_over_viewport(Control *p_overlay) { anim_editor->forward_force_draw_over_viewport(p_overlay); } + virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) { anim_editor->forward_canvas_force_draw_over_viewport(p_overlay); } AnimationPlayerEditorPlugin(EditorNode *p_node); ~AnimationPlayerEditorPlugin(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 34159964a5..2ea17fda1c 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -443,8 +443,12 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - if (canvas_item && !canvas_item->is_set_as_toplevel()) { - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + if (canvas_item) { + if (!canvas_item->is_set_as_toplevel()) { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, canvas_item->get_transform(), p_canvas_xform); + } } else { CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); @@ -610,8 +614,12 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n if (!lock_children || !editable) { for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - if (canvas_item && !canvas_item->is_set_as_toplevel()) { - _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + if (canvas_item) { + if (!canvas_item->is_set_as_toplevel()) { + _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + } else { + _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, canvas_item->get_transform(), p_canvas_xform); + } } else { CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node); _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform); @@ -707,6 +715,46 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 return (p_control->get_transform().xform(position) - parent_rect.position) / parent_rect.size; } +void CanvasItemEditor::_save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List<float> *p_bones_length, List<Dictionary> *p_bones_state) { + if (p_bones_length) + *p_bones_length = List<float>(); + if (p_bones_state) + *p_bones_state = List<Dictionary>(); + + const Node2D *bone = Object::cast_to<Node2D>(p_canvas_item); + if (bone && bone->has_meta("_edit_bone_")) { + // Check if we have an IK chain + List<const Node2D *> bone_ik_list; + bool ik_found = false; + bone = Object::cast_to<Node2D>(bone->get_parent()); + while (bone) { + bone_ik_list.push_back(bone); + if (bone->has_meta("_edit_ik_")) { + ik_found = true; + break; + } else if (!bone->has_meta("_edit_bone_")) { + break; + } + bone = Object::cast_to<Node2D>(bone->get_parent()); + } + + //Save the bone state and length if we have an IK chain + if (ik_found) { + bone = Object::cast_to<Node2D>(p_canvas_item); + Transform2D bone_xform = bone->get_global_transform(); + for (List<const Node2D *>::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) { + bone_xform = bone_xform * bone->get_transform().affine_inverse(); + const Node2D *parent_bone = bone_E->get(); + if (p_bones_length) + p_bones_length->push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position())); + if (p_bones_state) + p_bones_state->push_back(parent_bone->_edit_get_state()); + bone = parent_bone; + } + } + } +} + void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) { for (List<CanvasItem *>::Element *E = p_canvas_items.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); @@ -719,54 +767,28 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items } else { se->pre_drag_rect = Rect2(); } - se->pre_drag_bones_length = List<float>(); - se->pre_drag_bones_undo_state = List<Dictionary>(); // If we have a bone, save the state of all nodes in the IK chain - Node2D *bone = Object::cast_to<Node2D>(canvas_item); - if (bone && bone->has_meta("_edit_bone_")) { - // Check if we have an IK chain - List<Node2D *> bone_ik_list; - bool ik_found = false; - bone = Object::cast_to<Node2D>(bone->get_parent()); - while (bone) { - bone_ik_list.push_back(bone); - if (bone->has_meta("_edit_ik_")) { - ik_found = true; - break; - } else if (!bone->has_meta("_edit_bone_")) { - break; - } - bone = Object::cast_to<Node2D>(bone->get_parent()); - } - - //Save the bone state and length if we have an IK chain - if (ik_found) { - bone = Object::cast_to<Node2D>(canvas_item); - Transform2D bone_xform = bone->get_global_transform(); - for (List<Node2D *>::Element *bone_E = bone_ik_list.front(); bone_E; bone_E = bone_E->next()) { - bone_xform = bone_xform * bone->get_transform().affine_inverse(); - Node2D *parent_bone = bone_E->get(); - se->pre_drag_bones_length.push_back(parent_bone->get_global_transform().get_origin().distance_to(bone->get_global_position())); - se->pre_drag_bones_undo_state.push_back(parent_bone->_edit_get_state()); - bone = parent_bone; - } - } - } + _save_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_length), &(se->pre_drag_bones_undo_state)); } } } +void CanvasItemEditor::_restore_canvas_item_ik_chain(CanvasItem *p_canvas_item, const List<Dictionary> *p_bones_state) { + CanvasItem *canvas_item = p_canvas_item; + for (const List<Dictionary>::Element *E = p_bones_state->front(); E; E = E->next()) { + canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); + canvas_item->_edit_set_state(E->get()); + } +} + void CanvasItemEditor::_restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones) { for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); canvas_item->_edit_set_state(se->undo_state); if (restore_bones) { - for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) { - canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); - canvas_item->_edit_set_state(E->get()); - } + _restore_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_undo_state)); } } } @@ -1191,73 +1213,72 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_solve_IK(Node2D *leaf_node, Point2 target_position) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(leaf_node); - if (se && !se->pre_drag_bones_undo_state.empty()) { - - // Build the node list - Point2 leaf_pos = target_position; - - List<Node2D *> joints_list; - List<Point2> joints_pos; - Node2D *joint = leaf_node; - Transform2D joint_transform = leaf_node->get_global_transform_with_canvas(); - for (int i = 0; i < se->pre_drag_bones_undo_state.size() + 1; i++) { - joints_list.push_back(joint); - joints_pos.push_back(joint_transform.get_origin()); - joint_transform = joint_transform * joint->get_transform().affine_inverse(); - joint = Object::cast_to<Node2D>(joint->get_parent()); - } - Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin(); - - // Restraints the node to a maximum distance is necessary - float total_len = 0; - for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { - total_len += E->get(); - } - if ((root_pos.distance_to(leaf_pos)) > total_len) { - Vector2 rel = leaf_pos - root_pos; - rel = rel.normalized() * total_len; - leaf_pos = root_pos + rel; - } - joints_pos[0] = leaf_pos; - - // Run the solver - int solver_iterations = 64; - float solver_k = 0.3; - - // Build the position list - for (int i = 0; i < solver_iterations; i++) { - // Handle the leaf joint - int node_id = 0; + if (se) { + int nb_bones = se->pre_drag_bones_undo_state.size(); + if (nb_bones > 0) { + + // Build the node list + Point2 leaf_pos = target_position; + + List<Node2D *> joints_list; + List<Point2> joints_pos; + Node2D *joint = leaf_node; + Transform2D joint_transform = leaf_node->get_global_transform_with_canvas(); + for (int i = 0; i < nb_bones + 1; i++) { + joints_list.push_back(joint); + joints_pos.push_back(joint_transform.get_origin()); + joint_transform = joint_transform * joint->get_transform().affine_inverse(); + joint = Object::cast_to<Node2D>(joint->get_parent()); + } + Point2 root_pos = joints_list.back()->get()->get_global_transform_with_canvas().get_origin(); + + // Restraints the node to a maximum distance is necessary + float total_len = 0; for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { - Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized(); - int len = E->get(); - if (E == se->pre_drag_bones_length.front()) { - joints_pos[1] = joints_pos[1].linear_interpolate(joints_pos[0] + len * direction, solver_k); - } else if (E == se->pre_drag_bones_length.back()) { - joints_pos[node_id] = joints_pos[node_id].linear_interpolate(joints_pos[node_id + 1] - len * direction, solver_k); - } else { - Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0; - joints_pos[node_id] = joints_pos[node_id].linear_interpolate(center - (direction * len) / 2.0, solver_k); - joints_pos[node_id + 1] = joints_pos[node_id + 1].linear_interpolate(center + (direction * len) / 2.0, solver_k); + total_len += E->get(); + } + if ((root_pos.distance_to(leaf_pos)) > total_len) { + Vector2 rel = leaf_pos - root_pos; + rel = rel.normalized() * total_len; + leaf_pos = root_pos + rel; + } + joints_pos[0] = leaf_pos; + + // Run the solver + int solver_iterations = 64; + float solver_k = 0.3; + + // Build the position list + for (int i = 0; i < solver_iterations; i++) { + // Handle the leaf joint + int node_id = 0; + for (List<float>::Element *E = se->pre_drag_bones_length.front(); E; E = E->next()) { + Vector2 direction = (joints_pos[node_id + 1] - joints_pos[node_id]).normalized(); + int len = E->get(); + if (E == se->pre_drag_bones_length.front()) { + joints_pos[1] = joints_pos[1].linear_interpolate(joints_pos[0] + len * direction, solver_k); + } else if (E == se->pre_drag_bones_length.back()) { + joints_pos[node_id] = joints_pos[node_id].linear_interpolate(joints_pos[node_id + 1] - len * direction, solver_k); + } else { + Vector2 center = (joints_pos[node_id + 1] + joints_pos[node_id]) / 2.0; + joints_pos[node_id] = joints_pos[node_id].linear_interpolate(center - (direction * len) / 2.0, solver_k); + joints_pos[node_id + 1] = joints_pos[node_id + 1].linear_interpolate(center + (direction * len) / 2.0, solver_k); + } + node_id++; } - node_id++; } - } - // Set the position - float total_rot = 0.0f; - for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) { - Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized(); - Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized(); - float rot = current.angle_to(target); - if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) { - rot = -rot; + // Set the position + for (int node_id = joints_list.size() - 1; node_id > 0; node_id--) { + Point2 current = (joints_list[node_id - 1]->get_global_position() - joints_list[node_id]->get_global_position()).normalized(); + Point2 target = (joints_pos[node_id - 1] - joints_list[node_id]->get_global_position()).normalized(); + float rot = current.angle_to(target); + if (joints_list[node_id]->get_global_transform().basis_determinant() < 0) { + rot = -rot; + } + joints_list[node_id]->rotate(rot); } - joints_list[node_id]->rotate(rot); - total_rot += rot; } - - joints_list[0]->rotate(-total_rot); } } @@ -1656,7 +1677,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { // Resize the node if (m.is_valid()) { - _restore_canvas_item_state(drag_selection, true); + _restore_canvas_item_state(drag_selection); CanvasItem *canvas_item = drag_selection[0]; drag_to = transform.affine_inverse().xform(m->get_position()); @@ -1722,6 +1743,15 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_MOVE) { // Move the nodes if (m.is_valid()) { + + // Save the ik chain for reapplying before IK solve + Vector<List<Dictionary> > all_bones_ik_states; + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + List<Dictionary> bones_ik_states; + _save_canvas_item_ik_chain(E->get(), NULL, &bones_ik_states); + all_bones_ik_states.push_back(bones_ik_states); + } + _restore_canvas_item_state(drag_selection, true); drag_to = transform.affine_inverse().xform(m->get_position()); @@ -1743,6 +1773,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { } bool force_no_IK = m->get_alt(); + int index = 0; for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); @@ -1750,10 +1781,15 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { Node2D *node2d = Object::cast_to<Node2D>(canvas_item); if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) { + real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index])); + real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation); _solve_IK(node2d, new_pos); } else { canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); } + index++; } return true; } @@ -1792,6 +1828,14 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_selection.size() > 0) { + // Save the ik chain for reapplying before IK solve + Vector<List<Dictionary> > all_bones_ik_states; + for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { + List<Dictionary> bones_ik_states; + _save_canvas_item_ik_chain(E->get(), NULL, &bones_ik_states); + all_bones_ik_states.push_back(bones_ik_states); + } + _restore_canvas_item_state(drag_selection, true); bool move_local_base = k->get_alt(); @@ -1837,6 +1881,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { new_pos = previous_pos + (drag_to - drag_from); } + int index = 0; for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); @@ -1844,10 +1889,15 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { Node2D *node2d = Object::cast_to<Node2D>(canvas_item); if (node2d && se->pre_drag_bones_undo_state.size() > 0) { + real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index])); + real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation(); + node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation); _solve_IK(node2d, new_pos); } else { canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos)); } + index++; } } return true; @@ -2938,13 +2988,13 @@ void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p float offset = 0; Ref<Texture> lock = get_icon("LockViewport", "EditorIcons"); - if (p_node->has_meta("_edit_lock_")) { + if (p_node->has_meta("_edit_lock_") && show_edit_locks) { lock->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0)); offset += lock->get_size().x; } Ref<Texture> group = get_icon("GroupViewport", "EditorIcons"); - if (canvas_item->has_meta("_edit_group_")) { + if (canvas_item->has_meta("_edit_group_") && show_edit_locks) { group->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0)); //offset += group->get_size().x; } @@ -3018,6 +3068,7 @@ bool CanvasItemEditor::_build_bones_list(Node *p_node) { } void CanvasItemEditor::_draw_viewport() { + // Update the transform transform = Transform2D(); transform.scale_basis(Size2(zoom, zoom)); @@ -3066,11 +3117,11 @@ void CanvasItemEditor::_draw_viewport() { EditorPluginList *over_plugin_list = editor->get_editor_plugins_over(); if (!over_plugin_list->empty()) { - over_plugin_list->forward_draw_over_viewport(viewport); + over_plugin_list->forward_canvas_draw_over_viewport(viewport); } EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); if (!force_over_plugin_list->empty()) { - force_over_plugin_list->forward_force_draw_over_viewport(viewport); + force_over_plugin_list->forward_canvas_force_draw_over_viewport(viewport); } _draw_bones(); @@ -3543,6 +3594,12 @@ void CanvasItemEditor::_popup_callback(int p_op) { view_menu->get_popup()->set_item_checked(idx, show_viewport); viewport->update(); } break; + case SHOW_EDIT_LOCKS: { + show_edit_locks = !show_edit_locks; + int idx = view_menu->get_popup()->get_item_index(SHOW_EDIT_LOCKS); + view_menu->get_popup()->set_item_checked(idx, show_edit_locks); + viewport->update(); + } break; case SNAP_USE_NODE_PARENT: { snap_node_parent = !snap_node_parent; int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); @@ -4146,6 +4203,7 @@ Dictionary CanvasItemEditor::get_state() const { state["show_rulers"] = show_rulers; state["show_guides"] = show_guides; state["show_helpers"] = show_helpers; + state["show_edit_locks"] = show_edit_locks; state["snap_rotation"] = snap_rotation; state["snap_relative"] = snap_relative; state["snap_pixel"] = snap_pixel; @@ -4265,6 +4323,12 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { view_menu->get_popup()->set_item_checked(idx, show_helpers); } + if (state.has("show_edit_locks")) { + show_edit_locks = state["show_edit_locks"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_EDIT_LOCKS); + view_menu->get_popup()->set_item_checked(idx, show_edit_locks); + } + if (state.has("snap_rotation")) { snap_rotation = state["snap_rotation"]; int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); @@ -4412,13 +4476,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W)); move_button->set_tooltip(TTR("Move Mode")); - scale_button = memnew(ToolButton); - hb->add_child(scale_button); - scale_button->set_toggle_mode(true); - scale_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SCALE)); - scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), KEY_S)); - scale_button->set_tooltip(TTR("Scale Mode")); - rotate_button = memnew(ToolButton); hb->add_child(rotate_button); rotate_button->set_toggle_mode(true); @@ -4426,6 +4483,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E)); rotate_button->set_tooltip(TTR("Rotate Mode")); + scale_button = memnew(ToolButton); + hb->add_child(scale_button); + scale_button->set_toggle_mode(true); + scale_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SCALE)); + scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), KEY_S)); + scale_button->set_tooltip(TTR("Scale Mode")); + hb->add_child(memnew(VSeparator)); list_select_button = memnew(ToolButton); @@ -4535,6 +4599,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_edit_locks", TTR("Show Group And Lock Icons")), SHOW_EDIT_LOCKS); + p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION); @@ -4625,6 +4691,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { show_helpers = false; show_rulers = true; show_guides = true; + show_edit_locks = true; zoom = 1; view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH); previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen @@ -4917,7 +4984,6 @@ void CanvasItemEditorViewport::_perform_drop_data() { // Without root dropping multiple files is not allowed if (!target_node && selected_files.size() > 1) { - accept->get_ok()->set_text(TTR("Ok")); accept->set_text(TTR("Cannot instantiate multiple nodes without root.")); accept->popup_centered_minsize(); return; @@ -4979,7 +5045,6 @@ void CanvasItemEditorViewport::_perform_drop_data() { files_str += error_files[i].get_file().get_basename() + ","; } files_str = files_str.substr(0, files_str.length() - 1); - accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); accept->popup_centered_minsize(); } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 61d77581d3..2d539d6727 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -71,8 +71,7 @@ class CanvasItemEditor : public VBoxContainer { GDCLASS(CanvasItemEditor, VBoxContainer); - EditorNode *editor; - +public: enum Tool { TOOL_SELECT, TOOL_LIST_SELECT, @@ -84,6 +83,9 @@ class CanvasItemEditor : public VBoxContainer { TOOL_MAX }; +private: + EditorNode *editor; + enum MenuOption { SNAP_USE, SNAP_USE_NODE_PARENT, @@ -103,6 +105,7 @@ class CanvasItemEditor : public VBoxContainer { SHOW_GUIDES, SHOW_ORIGIN, SHOW_VIEWPORT, + SHOW_EDIT_LOCKS, LOCK_SELECTED, UNLOCK_SELECTED, GROUP_SELECTED, @@ -223,6 +226,7 @@ class CanvasItemEditor : public VBoxContainer { bool show_origin; bool show_viewport; bool show_helpers; + bool show_edit_locks; float zoom; Point2 view_offset; Point2 previous_update_view_offset; @@ -373,7 +377,9 @@ class CanvasItemEditor : public VBoxContainer { void _add_canvas_item(CanvasItem *p_canvas_item); + void _save_canvas_item_ik_chain(const CanvasItem *p_canvas_item, List<float> *p_bones_length, List<Dictionary> *p_bones_state); void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false); + void _restore_canvas_item_ik_chain(CanvasItem *p_canvas_item, const List<Dictionary> *p_bones_state); void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false); void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false); @@ -535,6 +541,8 @@ public: Control *get_viewport_control() { return viewport; } + Tool get_current_tool() { return tool; } + void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } void edit(CanvasItem *p_canvas_item); diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 9e052bb027..e3f364790a 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -415,7 +415,7 @@ void CollisionShape2DEditor::_get_current_shape_type() { canvas_item_editor->get_viewport_control()->update(); } -void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node) { return; diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h index 10784f1129..fb7b2acb0f 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.h +++ b/editor/plugins/collision_shape_2d_editor_plugin.h @@ -75,7 +75,7 @@ protected: public: bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_node); CollisionShape2DEditor(EditorNode *p_editor); @@ -89,7 +89,7 @@ class CollisionShape2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return collision_shape_2d_editor->forward_canvas_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { return collision_shape_2d_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { return collision_shape_2d_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "CollisionShape2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 72a746e95b..7f83865777 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -78,7 +78,11 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { +bool EditorTexturePreviewPlugin::should_generate_small_preview() const { + return true; +} + +Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Image> img; Ref<AtlasTexture> atex = p_from; @@ -100,8 +104,6 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { img = img->duplicate(); img->clear_mipmaps(); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; if (img->is_compressed()) { if (img->decompress() != OK) return Ref<Texture>(); @@ -109,22 +111,15 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { img->convert(Image::FORMAT_RGBA8); } - int width, height; - if (img->get_width() > thumbnail_size && img->get_width() >= img->get_height()) { - - width = thumbnail_size; - height = img->get_height() * thumbnail_size / img->get_width(); - } else if (img->get_height() > thumbnail_size && img->get_height() >= img->get_width()) { - - height = thumbnail_size; - width = img->get_width() * thumbnail_size / img->get_height(); - } else { - - width = img->get_width(); - height = img->get_height(); + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); + } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); - img->resize(width, height); post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -143,7 +138,7 @@ bool EditorImagePreviewPlugin::handles(const String &p_type) const { return p_type == "Image"; } -Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Image> img = p_from; @@ -153,8 +148,6 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { img = img->duplicate(); img->clear_mipmaps(); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; if (img->is_compressed()) { if (img->decompress() != OK) return Ref<Image>(); @@ -162,22 +155,15 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { img->convert(Image::FORMAT_RGBA8); } - int width, height; - if (img->get_width() > thumbnail_size && img->get_width() >= img->get_height()) { - - width = thumbnail_size; - height = img->get_height() * thumbnail_size / img->get_width(); - } else if (img->get_height() > thumbnail_size && img->get_height() >= img->get_width()) { - - height = thumbnail_size; - width = img->get_width() * thumbnail_size / img->get_height(); - } else { - - width = img->get_width(); - height = img->get_height(); + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); - img->resize(width, height); post_process_preview(img); Ref<ImageTexture> ptex; @@ -190,6 +176,9 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { EditorImagePreviewPlugin::EditorImagePreviewPlugin() { } +bool EditorImagePreviewPlugin::should_generate_small_preview() const { + return true; +} //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////// bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { @@ -197,7 +186,7 @@ bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "BitMap"); } -Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<BitMap> bm = p_from; @@ -227,8 +216,6 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { img.instance(); img->create(bm->get_size().width, bm->get_size().height, 0, Image::FORMAT_L8, data); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; if (img->is_compressed()) { if (img->decompress() != OK) return Ref<Texture>(); @@ -236,22 +223,15 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { img->convert(Image::FORMAT_RGBA8); } - int width, height; - if (img->get_width() > thumbnail_size && img->get_width() >= img->get_height()) { - - width = thumbnail_size; - height = img->get_height() * thumbnail_size / img->get_width(); - } else if (img->get_height() > thumbnail_size && img->get_height() >= img->get_width()) { - - height = thumbnail_size; - width = img->get_width() * thumbnail_size / img->get_height(); - } else { - - width = img->get_width(); - height = img->get_height(); + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); - img->resize(width, height); post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -260,6 +240,10 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { return ptex; } +bool EditorBitmapPreviewPlugin::should_generate_small_preview() const { + return true; +} + EditorBitmapPreviewPlugin::EditorBitmapPreviewPlugin() { } @@ -269,12 +253,12 @@ bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "PackedScene"); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { - return generate_from_path(p_from->get_path()); + return generate_from_path(p_from->get_path(), p_size); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) const { +Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 p_size) const { String temp_path = EditorSettings::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text(); @@ -323,7 +307,11 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Material"); //any material } -Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) const { +bool EditorMaterialPreviewPlugin::should_generate_small_preview() const { + return true; +} + +Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Material> material = p_from; ERR_FAIL_COND_V(material.is_null(), Ref<Texture>()); @@ -346,10 +334,9 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) const { ERR_FAIL_COND_V(!img.is_valid(), Ref<ImageTexture>()); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; img->convert(Image::FORMAT_RGBA8); - img->resize(thumbnail_size, thumbnail_size); + int thumbnail_size = MAX(p_size.x, p_size.y); + img->resize(thumbnail_size, thumbnail_size, Image::INTERPOLATE_CUBIC); post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); ptex->create_from_image(img, 0); @@ -490,7 +477,7 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Script"); } -Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Script> scr = p_from; if (scr.is_null()) @@ -512,10 +499,9 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) const { int line = 0; int col = 0; - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; Ref<Image> img; img.instance(); + int thumbnail_size = MAX(p_size.x, p_size.y); img->create(thumbnail_size, thumbnail_size, 0, Image::FORMAT_RGBA8); Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color"); @@ -613,16 +599,15 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "AudioStream"); } -Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<AudioStream> stream = p_from; ERR_FAIL_COND_V(stream.is_null(), Ref<Texture>()); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; PoolVector<uint8_t> img; - int w = thumbnail_size; - int h = thumbnail_size; + + int w = p_size.x; + int h = p_size.y; img.resize(w * h * 3); PoolVector<uint8_t>::Write imgdata = img.write(); @@ -711,7 +696,7 @@ bool EditorMeshPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Mesh"); //any Mesh } -Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { Ref<Mesh> mesh = p_from; ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture>()); @@ -749,10 +734,17 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) const { VS::get_singleton()->instance_set_base(mesh_instance, RID()); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; img->convert(Image::FORMAT_RGBA8); - img->resize(thumbnail_size, thumbnail_size); + + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); + } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); + post_process_preview(img); Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture)); @@ -825,15 +817,12 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "DynamicFontData"); } -Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) const { +Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, const Size2 p_size) const { Ref<DynamicFontData> SampledFont; SampledFont.instance(); SampledFont->set_font_path(p_path); - int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; - Ref<DynamicFont> sampled_font; sampled_font.instance(); sampled_font->set_size(50); @@ -864,7 +853,15 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) c ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); img->convert(Image::FORMAT_RGBA8); - img->resize(thumbnail_size, thumbnail_size); + + Vector2 new_size = img->get_size(); + if (new_size.x > p_size.x) { + new_size = Vector2(p_size.x, new_size.y * p_size.x / new_size.x); + } + if (new_size.y > p_size.y) { + new_size = Vector2(new_size.x * p_size.y / new_size.y, p_size.y); + } + img->resize(new_size.x, new_size.y, Image::INTERPOLATE_CUBIC); post_process_preview(img); @@ -874,9 +871,9 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) c return ptex; } -Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) const { +Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { - return generate_from_path(p_from->get_path()); + return generate_from_path(p_from->get_path(), p_size); } EditorFontPreviewPlugin::EditorFontPreviewPlugin() { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 8bd7943383..ed5d2a3ecd 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -39,7 +39,8 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorTexturePreviewPlugin(); }; @@ -48,7 +49,8 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorImagePreviewPlugin(); }; @@ -57,7 +59,8 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorBitmapPreviewPlugin(); }; @@ -66,8 +69,8 @@ class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; - virtual Ref<Texture> generate_from_path(const String &p_path) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; + virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const; EditorPackedScenePreviewPlugin(); }; @@ -95,7 +98,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual bool should_generate_small_preview() const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMaterialPreviewPlugin(); ~EditorMaterialPreviewPlugin(); @@ -104,7 +108,7 @@ public: class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorScriptPreviewPlugin(); }; @@ -112,7 +116,7 @@ public: class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorAudioStreamPreviewPlugin(); }; @@ -139,7 +143,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMeshPreviewPlugin(); ~EditorMeshPreviewPlugin(); @@ -162,8 +166,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from) const; - virtual Ref<Texture> generate_from_path(const String &p_path) const; + virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; + virtual Ref<Texture> generate_from_path(const String &p_path, const Size2 p_size) const; EditorFontPreviewPlugin(); ~EditorFontPreviewPlugin(); diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 4f8a307cc1..2f2e1dae81 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -319,13 +319,11 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void LightOccluder2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void LightOccluder2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node || !node->get_occluder_polygon().is_valid()) return; - Control *vpc = canvas_item_editor->get_viewport_control(); - Vector<Vector2> poly; if (wip_active) @@ -353,9 +351,9 @@ void LightOccluder2DEditor::forward_draw_over_viewport(Control *p_overlay) { if (i == poly.size() - 1 && (!node->get_occluder_polygon()->is_closed() || wip_active)) { } else { - vpc->draw_line(point, next_point, col, 2); + p_overlay->draw_line(point, next_point, col, 2); } - vpc->draw_texture(handle, point - handle->get_size() * 0.5); + p_overlay->draw_texture(handle, point - handle->get_size() * 0.5); } } diff --git a/editor/plugins/light_occluder_2d_editor_plugin.h b/editor/plugins/light_occluder_2d_editor_plugin.h index 39de8b1020..a1962892ee 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.h +++ b/editor/plugins/light_occluder_2d_editor_plugin.h @@ -83,7 +83,7 @@ protected: public: Vector2 snap_point(const Vector2 &p_point) const; - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); bool forward_gui_input(const Ref<InputEvent> &p_event); void edit(Node *p_collision_polygon); LightOccluder2DEditor(EditorNode *p_editor); @@ -98,7 +98,7 @@ class LightOccluder2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return light_occluder_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { return light_occluder_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { return light_occluder_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "LightOccluder2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 96c1ad2f2b..88b3194490 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -288,7 +288,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void Path2DEditor::forward_draw_over_viewport(Control *p_overlay) { +void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node) return; diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h index 1e3955f84f..3a78657746 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -107,7 +107,7 @@ protected: public: bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_path2d); Path2DEditor(EditorNode *p_editor); }; @@ -121,7 +121,7 @@ class Path2DEditorPlugin : public EditorPlugin { public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return path2d_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { return path2d_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { return path2d_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "Path2D"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp index 1c3c000808..6341d7f2ef 100644 --- a/editor/plugins/physical_bone_plugin.cpp +++ b/editor/plugins/physical_bone_plugin.cpp @@ -69,15 +69,7 @@ PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) : hide(); } -PhysicalBoneEditor::~PhysicalBoneEditor() { - // TODO the spatial_editor_hb should be removed from SpatialEditor, but in this moment it's not possible - for (int i = spatial_editor_hb->get_child_count() - 1; 0 <= i; --i) { - Node *n = spatial_editor_hb->get_child(i); - spatial_editor_hb->remove_child(n); - memdelete(n); - } - memdelete(spatial_editor_hb); -} +PhysicalBoneEditor::~PhysicalBoneEditor() {} void PhysicalBoneEditor::set_selected(PhysicalBone *p_pb) { @@ -98,19 +90,17 @@ void PhysicalBoneEditor::show() { PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) : editor(p_editor), - selected(NULL) { - - physical_bone_editor = memnew(PhysicalBoneEditor(editor)); -} + selected(NULL), + physical_bone_editor(editor) {} void PhysicalBonePlugin::make_visible(bool p_visible) { if (p_visible) { - physical_bone_editor->show(); + physical_bone_editor.show(); } else { - physical_bone_editor->hide(); - physical_bone_editor->set_selected(NULL); + physical_bone_editor.hide(); + physical_bone_editor.set_selected(NULL); selected = NULL; } } @@ -119,5 +109,5 @@ void PhysicalBonePlugin::edit(Object *p_node) { selected = static_cast<PhysicalBone *>(p_node); // Trust it ERR_FAIL_COND(!selected); - physical_bone_editor->set_selected(selected); + physical_bone_editor.set_selected(selected); } diff --git a/editor/plugins/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h index e03d342709..e1f8c9ec47 100644 --- a/editor/plugins/physical_bone_plugin.h +++ b/editor/plugins/physical_bone_plugin.h @@ -64,7 +64,7 @@ class PhysicalBonePlugin : public EditorPlugin { EditorNode *editor; PhysicalBone *selected; - PhysicalBoneEditor *physical_bone_editor; + PhysicalBoneEditor physical_bone_editor; public: virtual String get_name() const { return "PhysicalBone"; } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index e155daa2fa..c8e7bfb74b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3133,7 +3133,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { error_dialog = memnew(AcceptDialog); add_child(error_dialog); - error_dialog->get_ok()->set_text(TTR("OK")); debugger = memnew(ScriptEditorDebugger(editor)); debugger->connect("goto_script_line", this, "_goto_script_line"); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index bdeeaa106d..d4ddaf274f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -317,6 +317,7 @@ void ScriptTextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); + _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("script_text_editor", "syntax_highlighter", 0)); break; } } @@ -1058,6 +1059,7 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { } // highlighter_menu->set_item_checked(p_idx, true); set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); + EditorSettings::get_singleton()->set_project_metadata("script_text_editor", "syntax_highlighter", p_idx); } void ScriptTextEditor::_bind_methods() { diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 51e58b712e..361271af89 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -426,7 +426,7 @@ void ShaderEditor::ensure_select_current() { void ShaderEditor::edit(const Ref<Shader> &p_shader) { - if (p_shader.is_null()) + if (p_shader.is_null() || !p_shader->is_text_shader()) return; shader = p_shader; @@ -606,7 +606,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { bool ShaderEditorPlugin::handles(Object *p_object) const { Shader *shader = Object::cast_to<Shader>(p_object); - return shader != NULL; + return shader != NULL && shader->is_text_shader(); } void ShaderEditorPlugin::make_visible(bool p_visible) { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index c2554acd49..3e6a0ae81a 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2312,12 +2312,12 @@ void SpatialEditorViewport::_draw() { EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over(); if (!over_plugin_list->empty()) { - over_plugin_list->forward_draw_over_viewport(surface); + over_plugin_list->forward_spatial_draw_over_viewport(surface); } EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over(); if (!force_over_plugin_list->empty()) { - force_over_plugin_list->forward_force_draw_over_viewport(surface); + force_over_plugin_list->forward_spatial_force_draw_over_viewport(surface); } if (surface->has_focus()) { @@ -2346,7 +2346,6 @@ void SpatialEditorViewport::_draw() { Point2 center = _point_to_screen(_edit.center); VisualServer::get_singleton()->canvas_item_add_line(ci, _edit.mouse_pos, center, Color(0.4, 0.7, 1.0, 0.8)); } - if (previewing) { Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); @@ -3267,7 +3266,6 @@ void SpatialEditorViewport::_perform_drop_data() { files_str += error_files[i].get_file().get_basename() + ","; } files_str = files_str.substr(0, files_str.length() - 1); - accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); accept->popup_centered_minsize(); } @@ -3348,7 +3346,6 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p if (root_node) { list.push_back(root_node); } else { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance a child at.")); accept->popup_centered_minsize(); _remove_preview(); @@ -3356,7 +3353,6 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p } } if (list.size() != 1) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); _remove_preview(); @@ -4421,6 +4417,8 @@ void SpatialEditor::_menu_item_pressed(int p_option) { void SpatialEditor::_init_indicators() { { + origin_enabled = true; + grid_enabled = true; indicator_mat.instance(); indicator_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); @@ -4461,10 +4459,6 @@ void SpatialEditor::_init_indicators() { VS::get_singleton()->instance_set_layer_mask(origin_instance, 1 << SpatialEditorViewport::GIZMO_GRID_LAYER); VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF); - - origin_enabled = true; - grid_enabled = true; - last_grid_snap = 1; } { @@ -5286,6 +5280,8 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SELECT]->set_tooltip(TTR("Select Mode (Q)") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection")); + hbc_menu->add_child(memnew(VSeparator)); + tool_button[TOOL_MODE_MOVE] = memnew(ToolButton); hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]); tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); @@ -5310,6 +5306,8 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SCALE]->set_tooltip(TTR("Scale Mode (R)")); + hbc_menu->add_child(memnew(VSeparator)); + tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton); hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]); tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); @@ -5330,8 +5328,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); - VSeparator *vs = memnew(VSeparator); - hbc_menu->add_child(vs); + hbc_menu->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); @@ -5353,8 +5350,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { sct = ED_GET_SHORTCUT("spatial_editor/snap").ptr()->get_as_text(); tool_option_button[TOOL_OPT_USE_SNAP]->set_tooltip(vformat(TTR("Snap Mode (%s)"), sct)); - vs = memnew(VSeparator); - hbc_menu->add_child(vs); + hbc_menu->add_child(memnew(VSeparator)); // Drag and drop support; preview_node = memnew(Spatial); @@ -5587,7 +5583,6 @@ void SpatialEditorPlugin::make_visible(bool p_visible) { spatial_editor->show(); spatial_editor->set_process(true); - spatial_editor->grab_focus(); } else { diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 0e35ba8517..b7317cd593 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -510,7 +510,6 @@ private: RID grid[3]; RID grid_instance[3]; bool grid_visible[3]; //currently visible - float last_grid_snap; bool grid_enable[3]; //should be always visible if true bool grid_enabled; diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 3bf4140591..4ff7046a35 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -66,6 +66,7 @@ void TextEditor::_change_syntax_highlighter(int p_idx) { el = el->next(); } set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); + EditorSettings::get_singleton()->set_project_metadata("text_editor", "syntax_highlighter", p_idx); } void TextEditor::_load_theme_settings() { @@ -298,7 +299,7 @@ void TextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); - set_syntax_highlighter(NULL); + _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("text_editor", "syntax_highlighter", 0)); break; } } diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index a0adbfccff..aa4338d775 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -686,7 +686,7 @@ void TileMapEditor::_erase_selection() { } } -void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { +void TileMapEditor::_draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { Ref<Texture> t = node->get_tileset()->tile_get_texture(p_cell); @@ -783,19 +783,19 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h modulate.a = 0.5; if (r.has_no_area()) - canvas_item_editor->draw_texture_rect(t, rect, false, modulate, p_transpose); + p_viewport->draw_texture_rect(t, rect, false, modulate, p_transpose); else - canvas_item_editor->draw_texture_rect_region(t, rect, r, modulate, p_transpose); + p_viewport->draw_texture_rect_region(t, rect, r, modulate, p_transpose); } -void TileMapEditor::_draw_fill_preview(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { +void TileMapEditor::_draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform) { PoolVector<Vector2> points = _bucket_fill(p_point, false, true); PoolVector<Vector2>::Read pr = points.read(); int len = points.size(); for (int i = 0; i < len; ++i) { - _draw_cell(p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform); + _draw_cell(p_viewport, p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform); } } @@ -1390,17 +1390,17 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } -void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { +void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!node) return; Transform2D cell_xf = node->get_cell_transform(); - Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform(); + Transform2D xform = p_overlay->get_canvas_transform() * node->get_global_transform(); Transform2D xform_inv = xform.affine_inverse(); - Size2 screen_size = canvas_item_editor->get_size(); + Size2 screen_size = p_overlay->get_size(); { Rect2 aabb; aabb.position = node->world_to_map(xform_inv.xform(Vector2())); @@ -1419,7 +1419,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 to = xform.xform(node->map_to_world(Vector2(i, si.position.y + si.size.y + 1))); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; } @@ -1439,7 +1439,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(i, j + 1), true) + ofs); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; @@ -1457,7 +1457,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 to = xform.xform(node->map_to_world(Vector2(si.position.x + si.size.x + 1, i))); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; @@ -1476,7 +1476,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(j + 1, i), true) + ofs); Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); - canvas_item_editor->draw_line(from, to, col, 1); + p_overlay->draw_line(from, to, col, 1); if (max_lines-- == 0) break; @@ -1493,7 +1493,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(rectangle.size.x + 1, rectangle.size.y + 1))))); points.push_back(xform.xform(node->map_to_world((rectangle.position + Point2(0, rectangle.size.y + 1))))); - canvas_item_editor->draw_colored_polygon(points, Color(0.2, 0.8, 1, 0.4)); + p_overlay->draw_colored_polygon(points, Color(0.2, 0.8, 1, 0.4)); } if (mouse_over) { @@ -1519,7 +1519,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { col = Color(1.0, 0.4, 0.2, 0.8); for (int i = 0; i < 4; i++) - canvas_item_editor->draw_line(endpoints[i], endpoints[(i + 1) % 4], col, 2); + p_overlay->draw_line(endpoints[i], endpoints[(i + 1) % 4], col, 2); bool bucket_preview = EditorSettings::get_singleton()->get("editors/tile_map/bucket_fill_preview"); if (tool == TOOL_SELECTING || tool == TOOL_PICKING || !bucket_preview) { @@ -1538,7 +1538,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) { - _draw_cell(ids[0], E->key(), flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, ids[0], E->key(), flip_h, flip_v, transpose, xform); } } else if (tool == TOOL_RECTANGLE_PAINT) { @@ -1551,7 +1551,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) { for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) { - _draw_cell(ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, ids[0], Point2i(j, i), flip_h, flip_v, transpose, xform); } } } else if (tool == TOOL_PASTING) { @@ -1573,7 +1573,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { TileData tcd = E->get(); - _draw_cell(tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform); + _draw_cell(p_overlay, tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform); } Rect2i duplicate = rectangle; @@ -1585,12 +1585,12 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(duplicate.size.x + 1, duplicate.size.y + 1))))); points.push_back(xform.xform(node->map_to_world((duplicate.position + Point2(0, duplicate.size.y + 1))))); - canvas_item_editor->draw_colored_polygon(points, Color(0.2, 1.0, 0.8, 0.2)); + p_overlay->draw_colored_polygon(points, Color(0.2, 1.0, 0.8, 0.2)); } else if (tool == TOOL_BUCKET) { Vector<int> tiles = get_selected_tiles(); - _draw_fill_preview(tiles[0], over_tile, flip_h, flip_v, transpose, xform); + _draw_fill_preview(p_overlay, tiles[0], over_tile, flip_h, flip_v, transpose, xform); } else { @@ -1599,7 +1599,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) { if (st.size() == 1 && st[0] == TileMap::INVALID_CELL) return; - _draw_cell(st[0], over_tile, flip_h, flip_v, transpose, xform); + _draw_cell(p_overlay, st[0], over_tile, flip_h, flip_v, transpose, xform); } } } diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index c824824d56..74aece6f47 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -168,8 +168,8 @@ class TileMapEditor : public VBoxContainer { void _select(const Point2i &p_from, const Point2i &p_to); void _erase_selection(); - void _draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); - void _draw_fill_preview(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); + void _draw_cell(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); + void _draw_fill_preview(Control *p_viewport, int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Transform2D &p_xform); void _clear_bucket_cache(); void _update_copydata(); @@ -206,7 +206,7 @@ public: HBoxContainer *get_toolbar() const { return toolbar; } bool forward_gui_input(const Ref<InputEvent> &p_event); - void forward_draw_over_viewport(Control *p_overlay); + void forward_canvas_draw_over_viewport(Control *p_overlay); void edit(Node *p_tile_map); @@ -225,7 +225,7 @@ protected: public: virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tile_map_editor->forward_gui_input(p_event); } - virtual void forward_draw_over_viewport(Control *p_overlay) { tile_map_editor->forward_draw_over_viewport(p_overlay); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { tile_map_editor->forward_canvas_draw_over_viewport(p_overlay); } virtual String get_name() const { return "TileMap"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 0d683ea0a0..39e50ec7f8 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -510,6 +510,17 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in } undo_redo->create_action("Nodes Connected"); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().to_node == to && E->get().to_port == p_to_index) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + } + } + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_do_method(this, "_update_graph"); @@ -932,7 +943,10 @@ public: class VisualShaderNodePluginDefaultEditor : public VBoxContainer { GDCLASS(VisualShaderNodePluginDefaultEditor, VBoxContainer) public: - void _property_changed(const String &prop, const Variant &p_value) { + void _property_changed(const String &prop, const Variant &p_value, bool p_changing = false) { + + if (p_changing) + return; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); @@ -979,7 +993,7 @@ public: } static void _bind_methods() { - ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed); + ClassDB::bind_method("_property_changed", &VisualShaderNodePluginDefaultEditor::_property_changed, DEFVAL(false)); ClassDB::bind_method("_node_changed", &VisualShaderNodePluginDefaultEditor::_node_changed); ClassDB::bind_method("_refresh_request", &VisualShaderNodePluginDefaultEditor::_refresh_request); } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index f494c84efa..91ab5b4dff 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -41,7 +41,6 @@ #include "core/translation.h" #include "core/version.h" #include "core/version_hash.gen.h" -#include "editor_initialize_ssl.h" #include "editor_scale.h" #include "editor_settings.h" #include "editor_themes.h" @@ -2059,8 +2058,6 @@ void ProjectListFilter::_bind_methods() { ProjectListFilter::ProjectListFilter() { - editor_initialize_certificates(); //for asset sharing - _current_filter = FILTER_NAME; filter_option = memnew(OptionButton); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index af6b998b28..ff6832177e 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -119,7 +119,6 @@ void SceneTreeDock::instance(const String &p_file) { if (!edited_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance a child at.")); accept->popup_centered_minsize(); return; @@ -142,7 +141,6 @@ void SceneTreeDock::instance_scenes(const Vector<String> &p_files, Node *p_paren if (!parent || !edited_scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance the scenes at.")); accept->popup_centered_minsize(); return; @@ -164,7 +162,6 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node Ref<PackedScene> sdata = ResourceLoader::load(p_files[i]); if (!sdata.is_valid()) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error loading scene from %s"), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -174,7 +171,6 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!instanced_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -185,7 +181,6 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node if (_cyclical_dependency_exists(edited_scene->get_filename(), instanced_scene)) { - accept->get_ok()->set_text(TTR("Ok")); accept->set_text(vformat(TTR("Cannot instance the scene '%s' because the current scene exists within one of its nodes."), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -233,7 +228,6 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) { Ref<PackedScene> sdata = ResourceLoader::load(p_file); if (!sdata.is_valid()) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error loading scene from %s"), p_file)); accept->popup_centered_minsize(); return; @@ -241,7 +235,6 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!instanced_scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), p_file)); accept->popup_centered_minsize(); return; @@ -416,7 +409,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (scene_tree->get_selected() == edited_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -477,7 +469,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (editor_selection->is_selected(edited_scene)) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -547,7 +538,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (editor_selection->is_selected(edited_scene)) { current_option = -1; - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -634,7 +624,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *scene = editor_data->get_edited_scene_root(); if (!scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done without a scene.")); accept->popup_centered_minsize(); break; @@ -643,7 +632,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { List<Node *> selection = editor_selection->get_selected_node_list(); if (selection.size() != 1) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); break; @@ -652,14 +640,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *tocopy = selection.front()->get(); if (tocopy == scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can not perform with the root node.")); accept->popup_centered_minsize(); break; } if (tocopy != editor_data->get_edited_scene_root() && tocopy->get_filename() != "") { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on instanced scenes.")); accept->popup_centered_minsize(); break; @@ -844,6 +830,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL); editor_data->get_undo_redo().commit_action(); + editor->edit_node(new_node); + } break; default: { @@ -1304,7 +1292,6 @@ bool SceneTreeDock::_validate_no_foreign() { if (E->get() != edited_scene && E->get()->get_owner() != edited_scene) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't operate on nodes from a foreign scene!")); accept->popup_centered_minsize(); return false; @@ -1312,7 +1299,6 @@ bool SceneTreeDock::_validate_no_foreign() { if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get())) >= 0) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't operate on nodes the current scene inherits from!")); accept->popup_centered_minsize(); return false; @@ -1792,14 +1778,12 @@ void SceneTreeDock::_new_scene_from(String p_file) { List<Node *> selection = editor_selection->get_selected_node_list(); if (selection.size() != 1) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); return; } if (EditorNode::get_singleton()->is_scene_open(p_file)) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't overwrite scene that is still open!")); accept->popup_centered_minsize(); return; @@ -1817,7 +1801,6 @@ void SceneTreeDock::_new_scene_from(String p_file) { memdelete(copy); if (err != OK) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Couldn't save new scene. Likely dependencies (instances) couldn't be satisfied.")); accept->popup_centered_minsize(); return; @@ -1829,14 +1812,12 @@ void SceneTreeDock::_new_scene_from(String p_file) { err = ResourceSaver::save(p_file, sdata, flg); if (err != OK) { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Error saving scene.")); accept->popup_centered_minsize(); return; } _replace_with_branch_scene(p_file, base); } else { - accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Error duplicating scene to save it.")); accept->popup_centered_minsize(); return; @@ -2422,7 +2403,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(create_dialog); create_dialog->connect("create", this, "_create"); create_dialog->connect("favorites_updated", this, "_update_create_root_dialog"); - EditorFileSystem::get_singleton()->connect("script_classes_updated", create_dialog, "_save_and_update_favorite_list"); rename_dialog = memnew(RenameDialog(scene_tree, &editor_data->get_undo_redo())); add_child(rename_dialog); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index be255ba4aa..d8de775d36 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -164,7 +164,6 @@ void ScriptCreateDialog::_create_new() { if (script_template != "") { scr = ResourceLoader::load(script_template); if (scr.is_null()) { - alert->get_ok()->set_text(TTR("OK")); alert->set_text(vformat(TTR("Error loading template '%s'"), script_template)); alert->popup_centered(); return; @@ -201,7 +200,6 @@ void ScriptCreateDialog::_load_exist() { String path = file_path->get_text(); RES p_script = ResourceLoader::load(path, "Script"); if (p_script.is_null()) { - alert->get_ok()->set_text(TTR("OK")); alert->set_text(vformat(TTR("Error loading script from %s"), path)); alert->popup_centered(); return; diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index a28a111025..cc477314e9 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -492,17 +492,19 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da pinfo.usage = PropertyUsageFlags(int(prop[4])); Variant var = prop[5]; - String hint_string = pinfo.hint_string; - if (hint_string.begins_with("RES:") && hint_string != "RES:") { - String path = hint_string.substr(4, hint_string.length()); - var = ResourceLoader::load(path); - } - if (is_new_object) { //don't update.. it's the same, instead refresh debugObj->prop_list.push_back(pinfo); } + if (var.get_type() == Variant::STRING) { + String str = var; + var = str.substr(4, str.length()); + + if (str.begins_with("PATH")) + var = ResourceLoader::load(var); + } + debugObj->prop_values[pinfo.name] = var; } @@ -573,9 +575,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da PropertyHint h = PROPERTY_HINT_NONE; String hs = String(); - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); + if (v.get_type() == Variant::OBJECT) { h = PROPERTY_HINT_OBJECT_ID; String s = v; s = s.replace("[", ""); @@ -596,9 +596,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da PropertyHint h = PROPERTY_HINT_NONE; String hs = String(); - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); + if (v.get_type() == Variant::OBJECT) { h = PROPERTY_HINT_OBJECT_ID; String s = v; s = s.replace("[", ""); @@ -619,9 +617,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da PropertyHint h = PROPERTY_HINT_NONE; String hs = String(); - if (n.begins_with("*")) { - - n = n.substr(1, n.length()); + if (v.get_type() == Variant::OBJECT) { h = PROPERTY_HINT_OBJECT_ID; String s = v; s = s.replace("[", ""); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 97cdd43fee..fe384da75b 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -56,11 +56,7 @@ void EditorSettingsDialog::_settings_property_edited(const String &p_name) { String full_name = inspector->get_full_item_path(p_name); - // Small usability workaround to update the text color settings when the - // color theme is changed - if (full_name == "text_editor/theme/color_theme") { - inspector->get_inspector()->update_tree(); - } else if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") { + if (full_name == "interface/theme/accent_color" || full_name == "interface/theme/base_color" || full_name == "interface/theme/contrast") { EditorSettings::get_singleton()->set_manually("interface/theme/preset", "Custom"); // set preset to Custom } else if (full_name.begins_with("text_editor/highlighting")) { EditorSettings::get_singleton()->set_manually("text_editor/theme/color_theme", "Custom"); diff --git a/main/input_default.cpp b/main/input_default.cpp index 2efbb3f849..10be291b8d 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -598,7 +598,13 @@ Input::CursorShape InputDefault::get_default_cursor_shape() { void InputDefault::set_default_cursor_shape(CursorShape p_shape) { default_shape = p_shape; - OS::get_singleton()->set_cursor_shape((OS::CursorShape)p_shape); + // The default shape is set in Viewport::_gui_input_event. To instantly + // see the shape in the viewport we need to trigger a mouse motion event. + Ref<InputEventMouseMotion> mm; + mm.instance(); + mm->set_position(mouse_pos); + mm->set_global_position(mouse_pos); + parse_input_event(mm); } void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { diff --git a/main/main.cpp b/main/main.cpp index 90d4db2948..dac646ba70 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -73,66 +73,99 @@ #include "editor/doc/doc_data.h" #include "editor/doc/doc_data_class_path.gen.h" #include "editor/editor_node.h" +#include "editor/editor_settings.h" #include "editor/project_manager.h" #endif -static ProjectSettings *globals = NULL; +/* Static members */ + +// Singletons + +// Initialized in setup() static Engine *engine = NULL; +static ProjectSettings *globals = NULL; static InputMap *input_map = NULL; -static bool _start_success = false; -static ScriptDebugger *script_debugger = NULL; -AudioServer *audio_server = NULL; -ARVRServer *arvr_server = NULL; -PhysicsServer *physics_server = NULL; -Physics2DServer *physics_2d_server = NULL; - -static MessageQueue *message_queue = NULL; +static TranslationServer *translation_server = NULL; static Performance *performance = NULL; - static PackedData *packed_data = NULL; #ifdef MINIZIP_ENABLED static ZipArchive *zip_packed_data = NULL; #endif static FileAccessNetworkClient *file_access_network_client = NULL; -static TranslationServer *translation_server = NULL; +static ScriptDebugger *script_debugger = NULL; +static MessageQueue *message_queue = NULL; + +// Initialized in setup2() +static AudioServer *audio_server = NULL; +static ARVRServer *arvr_server = NULL; +static PhysicsServer *physics_server = NULL; +static Physics2DServer *physics_2d_server = NULL; +// We error out if setup2() doesn't turn this true +static bool _start_success = false; + +// Drivers + +static int video_driver_idx = -1; +static int audio_driver_idx = -1; + +// Engine config/tools + +static bool editor = false; +static bool project_manager = false; +static String locale; +static bool show_help = false; +static bool auto_build_solutions = false; +static bool auto_quit = false; +static OS::ProcessID allow_focus_steal_pid = 0; + +// Display static OS::VideoMode video_mode; +static int init_screen = -1; +static bool init_fullscreen = false; static bool init_maximized = false; static bool init_windowed = false; -static bool init_fullscreen = false; static bool init_always_on_top = false; static bool init_use_custom_pos = false; +static Vector2 init_custom_pos; +static bool force_lowdpi = false; +static bool use_vsync = true; + +// Debug + +static bool use_debug_profiler = false; #ifdef DEBUG_ENABLED static bool debug_collisions = false; static bool debug_navigation = false; #endif static int frame_delay = 0; -static Vector2 init_custom_pos; -static int video_driver_idx = -1; -static int audio_driver_idx = -1; -static String locale; -static bool use_debug_profiler = false; -static bool force_lowdpi = false; -static int init_screen = -1; -static bool use_vsync = true; -static bool editor = false; -static bool show_help = false; static bool disable_render_loop = false; static int fixed_fps = -1; -static bool auto_build_solutions = false; -static bool auto_quit = false; static bool print_fps = false; -static OS::ProcessID allow_focus_steal_pid = 0; - -static bool project_manager = false; +/* Helper methods */ +// Used by Mono module, should likely be registered in Engine singleton instead +// FIXME: This is also not 100% accurate, `project_manager` is only true when it was requested, +// but not if e.g. we fail to load and project and fallback to the manager. bool Main::is_project_manager() { return project_manager; } -void initialize_physics() { +static String unescape_cmdline(const String &p_str) { + return p_str.replace("%20", " "); +} + +static String get_full_version_string() { + String hash = String(VERSION_HASH); + if (hash.length() != 0) + hash = "." + hash.left(7); + return String(VERSION_FULL_BUILD) + hash; +} +// FIXME: Could maybe be moved to PhysicsServerManager and Physics2DServerManager directly +// to have less code in main.cpp. +void initialize_physics() { /// 3D Physics Server physics_server = PhysicsServerManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServerManager::setting_property_name)); if (!physics_server) { @@ -160,19 +193,6 @@ void finalize_physics() { memdelete(physics_2d_server); } -static String unescape_cmdline(const String &p_str) { - - return p_str.replace("%20", " "); -} - -static String get_full_version_string() { - - String hash = String(VERSION_HASH); - if (hash.length() != 0) - hash = "." + hash.left(7); - return String(VERSION_FULL_BUILD) + hash; -} - //#define DEBUG_INIT #ifdef DEBUG_INIT #define MAIN_PRINT(m_txt) print_line(m_txt) @@ -277,6 +297,32 @@ void Main::print_help(const char *p_binary) { #endif } +/* Engine initialization + * + * Consists of several methods that are called by each platform's specific main(argc, argv). + * To fully understand engine init, one should therefore start from the platform's main and + * see how it calls into the Main class' methods. + * + * The initialization is typically done in 3 steps (with the setup2 step triggered either + * automatically by setup, or manually in the platform's main). + * + * - setup(execpath, argc, argv, p_second_phase) is the main entry point for all platforms, + * responsible for the initialization of all low level singletons and core types, and parsing + * command line arguments to configure things accordingly. + * If p_second_phase is true, it will chain into setup2() (default behaviour). This is + * disabled on some platforms (Android, iOS, UWP) which trigger the second step in their + * own time. + * + * - setup2(p_main_tid_override) registers high level servers and singletons, displays the + * boot splash, then registers higher level types (scene, editor, etc.). + * + * - start() is the last step and that's where command line tools can run, or the main loop + * can be created eventually and the project settings put into action. That's also where + * the editor node is created, if relevant. + * start() does it own argument parsing for a subset of the command line arguments described + * in help, it's a bit messy and should be globalized with the setup() parsing somehow. + */ + Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) { RID_OwnerBase::init_rid(); @@ -756,7 +802,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (editor) { packed_data->set_disabled(true); globals->set_disable_feature_overrides(true); - StreamPeerSSL::initialize_certs = false; //will be initialized by editor } #endif @@ -1003,15 +1048,6 @@ error: if (file_access_network_client) memdelete(file_access_network_client); - // Note 1: *zip_packed_data live into *packed_data - // Note 2: PackedData::~PackedData destroy this. - /* -#ifdef MINIZIP_ENABLED - if (zip_packed_data) - memdelete( zip_packed_data ); -#endif -*/ - unregister_core_driver_types(); unregister_core_types(); @@ -1210,7 +1246,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } // everything the main loop needs to know about frame timings - static MainTimerSync main_timer_sync; bool Main::start() { @@ -1595,6 +1630,7 @@ bool Main::start() { sml->set_use_font_oversampling(font_oversampling); } else { + GLOBAL_DEF("display/window/stretch/mode", "disabled"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/mode", PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,2d,viewport")); GLOBAL_DEF("display/window/stretch/aspect", "ignore"); @@ -1654,6 +1690,10 @@ bool Main::start() { } if (!project_manager && !editor) { // game + + // Load SSL Certificates from Project Settings (or builtin) + StreamPeerSSL::load_certs_from_memory(StreamPeerSSL::get_project_cert_array()); + if (game_path != "") { Node *scene = NULL; Ref<PackedScene> scenedata = ResourceLoader::load(local_game_path); @@ -1686,6 +1726,15 @@ bool Main::start() { sml->get_root()->add_child(pmanager); OS::get_singleton()->set_context(OS::CONTEXT_PROJECTMAN); } + + if (project_manager || editor) { + // Load SSL Certificates from Editor Settings (or builtin) + String certs = EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String(); + if (certs != "") + StreamPeerSSL::load_certs_from_file(certs); + else + StreamPeerSSL::load_certs_from_memory(StreamPeerSSL::get_project_cert_array()); + } #endif } @@ -1699,13 +1748,23 @@ bool Main::start() { return true; } +/* Main iteration + * + * This is the iteration of the engine's game loop, advancing the state of physics, + * rendering and audio. + * It's called directly by the platform's OS::run method, where the loop is created + * and monitored. + * + * The OS implementation can impact its draw step with the Main::force_redraw() method. + */ + uint64_t Main::last_ticks = 0; uint64_t Main::target_ticks = 0; uint32_t Main::frames = 0; uint32_t Main::frame = 0; bool Main::force_redraw_requested = false; -//for performance metrics +// For performance metrics static uint64_t physics_process_max = 0; static uint64_t idle_process_max = 0; @@ -1729,11 +1788,6 @@ bool Main::iteration() { Engine::get_singleton()->_frame_step = step; - /* - if (time_accum+step < frame_slice) - return false; - */ - uint64_t physics_process_ticks = 0; uint64_t idle_process_ticks = 0; @@ -1877,9 +1931,15 @@ bool Main::iteration() { } void Main::force_redraw() { - force_redraw_requested = true; -}; +} + +/* Engine deinitialization + * + * Responsible for freeing all the memory allocated by previous setup steps, + * so that the engine closes cleanly without leaking memory or crashing. + * The order matters as some of those steps are linked with each other. + */ void Main::cleanup() { diff --git a/main/main.h b/main/main.h index bd56e21d94..23a19dddec 100644 --- a/main/main.h +++ b/main/main.h @@ -49,13 +49,16 @@ class Main { static bool force_redraw_requested; public: + static bool is_project_manager(); + static Error setup(const char *execpath, int argc, char *argv[], bool p_second_phase = true); static Error setup2(Thread::ID p_main_tid_override = 0); static bool start(); + static bool iteration(); - static void cleanup(); static void force_redraw(); - static bool is_project_manager(); + + static void cleanup(); }; -#endif +#endif // MAIN_H diff --git a/modules/bullet/godot_collision_configuration.cpp b/modules/bullet/godot_collision_configuration.cpp index f4bb9acbd7..919c3152d7 100644 --- a/modules/bullet/godot_collision_configuration.cpp +++ b/modules/bullet/godot_collision_configuration.cpp @@ -94,3 +94,59 @@ btCollisionAlgorithmCreateFunc *GodotCollisionConfiguration::getClosestPointsAlg return btDefaultCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(proxyType0, proxyType1); } } + +GodotSoftCollisionConfiguration::GodotSoftCollisionConfiguration(const btDiscreteDynamicsWorld *world, const btDefaultCollisionConstructionInfo &constructionInfo) : + btSoftBodyRigidBodyCollisionConfiguration(constructionInfo) { + + void *mem = NULL; + + mem = btAlignedAlloc(sizeof(GodotRayWorldAlgorithm::CreateFunc), 16); + m_rayWorldCF = new (mem) GodotRayWorldAlgorithm::CreateFunc(world); + + mem = btAlignedAlloc(sizeof(GodotRayWorldAlgorithm::SwappedCreateFunc), 16); + m_swappedRayWorldCF = new (mem) GodotRayWorldAlgorithm::SwappedCreateFunc(world); +} + +GodotSoftCollisionConfiguration::~GodotSoftCollisionConfiguration() { + m_rayWorldCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree(m_rayWorldCF); + + m_swappedRayWorldCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree(m_swappedRayWorldCF); +} + +btCollisionAlgorithmCreateFunc *GodotSoftCollisionConfiguration::getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1) { + + if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0 && CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + // This collision is not supported + return m_emptyCreateFunc; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0) { + + return m_rayWorldCF; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + return m_swappedRayWorldCF; + } else { + + return btSoftBodyRigidBodyCollisionConfiguration::getCollisionAlgorithmCreateFunc(proxyType0, proxyType1); + } +} + +btCollisionAlgorithmCreateFunc *GodotSoftCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1) { + + if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0 && CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + // This collision is not supported + return m_emptyCreateFunc; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType0) { + + return m_rayWorldCF; + } else if (CUSTOM_CONVEX_SHAPE_TYPE == proxyType1) { + + return m_swappedRayWorldCF; + } else { + + return btSoftBodyRigidBodyCollisionConfiguration::getClosestPointsAlgorithmCreateFunc(proxyType0, proxyType1); + } +} diff --git a/modules/bullet/godot_collision_configuration.h b/modules/bullet/godot_collision_configuration.h index 9b30ad0c62..11012c5f6d 100644 --- a/modules/bullet/godot_collision_configuration.h +++ b/modules/bullet/godot_collision_configuration.h @@ -32,6 +32,7 @@ #define GODOT_COLLISION_CONFIGURATION_H #include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h> +#include <BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h> /** @author AndreaCatania @@ -50,4 +51,16 @@ public: virtual btCollisionAlgorithmCreateFunc *getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1); virtual btCollisionAlgorithmCreateFunc *getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1); }; + +class GodotSoftCollisionConfiguration : public btSoftBodyRigidBodyCollisionConfiguration { + btCollisionAlgorithmCreateFunc *m_rayWorldCF; + btCollisionAlgorithmCreateFunc *m_swappedRayWorldCF; + +public: + GodotSoftCollisionConfiguration(const btDiscreteDynamicsWorld *world, const btDefaultCollisionConstructionInfo &constructionInfo = btDefaultCollisionConstructionInfo()); + virtual ~GodotSoftCollisionConfiguration(); + + virtual btCollisionAlgorithmCreateFunc *getCollisionAlgorithmCreateFunc(int proxyType0, int proxyType1); + virtual btCollisionAlgorithmCreateFunc *getClosestPointsAlgorithmCreateFunc(int proxyType0, int proxyType1); +}; #endif diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 08d8b8c6f6..3b44ab838e 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -164,9 +164,11 @@ bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) } btScalar GodotClosestConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) { - btScalar res = btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); - m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID - return res; + if (convexResult.m_localShapeInfo) + m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID + else + m_shapeId = 0; + return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); } bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index f81cfe84fb..f24c8670a3 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -351,7 +351,7 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) { void RigidBodyBullet::dispatch_callbacks() { /// The check isTransformChanged is necessary in order to call integrated forces only when the first transform is sent - if ((btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && isTransformChanged) { + if ((btBody->isKinematicObject() || btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && isTransformChanged) { if (omit_forces_integration) btBody->clearForces(); @@ -774,10 +774,13 @@ Vector3 RigidBodyBullet::get_angular_velocity() const { void RigidBodyBullet::set_transform__bullet(const btTransform &p_global_transform) { if (mode == PhysicsServer::BODY_MODE_KINEMATIC) { + if (space) + btBody->setLinearVelocity((p_global_transform.getOrigin() - btBody->getWorldTransform().getOrigin()) / space->get_delta_time()); // The kinematic use MotionState class godotMotionState->moveBody(p_global_transform); } btBody->setWorldTransform(p_global_transform); + scratch(); } const btTransform &RigidBodyBullet::get_transform__bullet() const { diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 5b220e1039..329e12cfff 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -150,14 +150,14 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra return btQuery.m_count; } -bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) { +bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) { ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape); btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin); if (!btShape->isConvex()) { bulletdelete(btShape); ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); - return 0; + return false; } btConvexShape *bt_convex_shape = static_cast<btConvexShape *>(btShape); @@ -177,10 +177,13 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, 0.002); + r_closest_unsafe = 1.0; + r_closest_safe = 1.0; + if (btResult.hasHit()) { const btScalar l = bt_motion.length(); - p_closest_unsafe = btResult.m_closestHitFraction; - p_closest_safe = MAX(p_closest_unsafe - (1 - ((l - 0.01) / l)), 0); + r_closest_unsafe = btResult.m_closestHitFraction; + r_closest_safe = MAX(r_closest_unsafe - (1 - ((l - 0.01) / l)), 0); if (r_info) { if (btCollisionObject::CO_RIGID_BODY == btResult.m_hitCollisionObject->getInternalType()) { B_TO_G(static_cast<const btRigidBody *>(btResult.m_hitCollisionObject)->getVelocityInLocalPoint(btResult.m_hitPointWorld), r_info->linear_velocity); @@ -195,7 +198,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf } bulletdelete(bt_convex_shape); - return btResult.hasHit(); + return true; // Mean success } /// Returns the list of contacts pairs in this order: Local contact, other body contact @@ -577,7 +580,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) { } if (p_create_soft_world) { - collisionConfiguration = bulletnew(btSoftBodyRigidBodyCollisionConfiguration); + collisionConfiguration = bulletnew(GodotSoftCollisionConfiguration(static_cast<btDiscreteDynamicsWorld *>(world_mem))); } else { collisionConfiguration = bulletnew(GodotCollisionConfiguration(static_cast<btDiscreteDynamicsWorld *>(world_mem))); } @@ -786,30 +789,32 @@ void SpaceBullet::check_body_collision() { if (numContacts) { btManifoldPoint &pt = contactManifold->getContactPoint(0); #endif - Vector3 collisionWorldPosition; - Vector3 collisionLocalPosition; - Vector3 normalOnB; - float appliedImpulse = pt.m_appliedImpulse; - B_TO_G(pt.m_normalWorldOnB, normalOnB); - - if (bodyA->can_add_collision()) { - B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition); - /// pt.m_localPointB Doesn't report the exact point in local space - B_TO_G(pt.getPositionWorldOnB() - contactManifold->getBody1()->getWorldTransform().getOrigin(), collisionLocalPosition); - bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, pt.m_index1, pt.m_index0); - } - if (bodyB->can_add_collision()) { - B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition); - /// pt.m_localPointA Doesn't report the exact point in local space - B_TO_G(pt.getPositionWorldOnA() - contactManifold->getBody0()->getWorldTransform().getOrigin(), collisionLocalPosition); - bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, pt.m_index0, pt.m_index1); - } + if (pt.getDistance() <= 0.0) { + Vector3 collisionWorldPosition; + Vector3 collisionLocalPosition; + Vector3 normalOnB; + float appliedImpulse = pt.m_appliedImpulse; + B_TO_G(pt.m_normalWorldOnB, normalOnB); + + if (bodyA->can_add_collision()) { + B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition); + /// pt.m_localPointB Doesn't report the exact point in local space + B_TO_G(pt.getPositionWorldOnB() - contactManifold->getBody1()->getWorldTransform().getOrigin(), collisionLocalPosition); + bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, pt.m_index1, pt.m_index0); + } + if (bodyB->can_add_collision()) { + B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition); + /// pt.m_localPointA Doesn't report the exact point in local space + B_TO_G(pt.getPositionWorldOnA() - contactManifold->getBody0()->getWorldTransform().getOrigin(), collisionLocalPosition); + bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, pt.m_index0, pt.m_index1); + } #ifdef DEBUG_ENABLED - if (is_debugging_contacts()) { - add_debug_contact(collisionWorldPosition); - } + if (is_debugging_contacts()) { + add_debug_contact(collisionWorldPosition); + } #endif + } } } } diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 0649e1f7e3..67ab5c610d 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -78,7 +78,7 @@ public: virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false); virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); - virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL); + virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL); /// Returns the list of contacts pairs in this order: Local contact, other body contact virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false); diff --git a/modules/gdnative/arvr/config.py b/modules/gdnative/arvr/config.py index 4d1bdfe4d1..53bc827027 100644 --- a/modules/gdnative/arvr/config.py +++ b/modules/gdnative/arvr/config.py @@ -1,4 +1,4 @@ -def can_build(platform): +def can_build(env, platform): return True def configure(env): diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d12c1f555c..b0d5422afe 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -126,10 +126,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco GDScriptLanguage::singleton->lock->unlock(); #endif - if (r_error.error != Variant::CallError::CALL_OK) { - memdelete(instance); - ERR_FAIL_COND_V(r_error.error != Variant::CallError::CALL_OK, NULL); //error constructing - } + ERR_FAIL_COND_V(r_error.error != Variant::CallError::CALL_OK, NULL); //error constructing } //@TODO make thread safe diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 741b837b05..310c4e21f2 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -388,7 +388,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, an->elements[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -419,7 +419,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, dn->elements[i].key, slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -429,7 +429,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ret = _parse_expression(codegen, dn->elements[i].value, slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -545,7 +545,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -578,7 +578,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -606,7 +606,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -655,7 +655,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } @@ -681,7 +681,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int ret = _parse_expression(codegen, on->arguments[i], slevel); if (ret < 0) return ret; - if (ret & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) { + if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) { slevel++; codegen.alloc_stack(slevel); } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 32a7668760..a9b641de50 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1189,6 +1189,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c c.line = op->line; c.block = blk; if (_guess_expression_type(p_context, op->arguments[1], r_type)) { + r_type.type.is_meta_type = false; return true; } } @@ -1221,7 +1222,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c int def_from = p_context.function->arguments.size() - p_context.function->default_values.size(); if (i >= def_from) { - int def_idx = def_from - i; + int def_idx = i - def_from; if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) { const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]); if (op->arguments.size() < 2) { @@ -1376,11 +1377,11 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_ for (int i = 0; i < base_type.class_type->variables.size(); i++) { GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i]; if (m.identifier == p_identifier) { - if (m.data_type.has_type) { - r_type.type = m.data_type; - return true; - } if (m.expression) { + if (p_context.line == m.expression->line) { + // Variable used in the same expression + return false; + } if (_guess_expression_type(p_context, m.expression, r_type)) { return true; } @@ -1389,6 +1390,10 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_ return true; } } + if (m.data_type.has_type) { + r_type.type = m.data_type; + return true; + } return false; } } diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index c469defb01..5af9bbc05f 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -1458,7 +1458,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; case MATH_ATAN2: { - MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "x"), PropertyInfo(Variant::REAL, "y")); + MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "y"), PropertyInfo(Variant::REAL, "x")); mi.return_val.type = Variant::REAL; return mi; } break; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 89e102a858..ea1287374b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -81,8 +81,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } tokenizer->advance(); - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { + if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) { + return false; + } + if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { // be more python-like int current = tab_level.back()->get(); tab_level.push_back(current); @@ -92,10 +95,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } while (true) { - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { return false; //wtf + } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) { + return false; } else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) { int indent = tokenizer->get_token_line_indent(); @@ -637,9 +641,21 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = op; } else { - - _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); - return NULL; + // Object is a special case + bool valid = false; + if (bi_type == Variant::OBJECT) { + int object_constant = ClassDB::get_integer_constant("Object", identifier, &valid); + if (valid) { + ConstantNode *cn = alloc_node<ConstantNode>(); + cn->value = object_constant; + cn->datatype = _type_from_variant(cn->value); + expr = cn; + } + } + if (!valid) { + _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + "."); + return NULL; + } } } else { @@ -4443,7 +4459,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { continue; } break; case GDScriptTokenizer::TK_PR_SLAVE: +#ifdef DEBUG_ENABLED _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet"); +#endif case GDScriptTokenizer::TK_PR_PUPPET: { //may be fallthrough from export, ignore if so @@ -4849,6 +4867,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { StringName const_id = tokenizer->get_token_literal(); + if (current_class->constant_expressions.has(const_id)) { + _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->constant_expressions[const_id].expression->line) + ")."); + return; + } + + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == const_id) { + _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->variables[i].line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { @@ -7202,6 +7234,12 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { expr.is_constant = true; c.type = expr; c.expression->set_datatype(expr); + + DataType tmp; + if (_get_member_type(p_class->base_type, E->key(), tmp)) { + _set_error("Member '" + String(E->key()) + "' already exists in parent class.", c.expression->line); + return; + } } // Function declarations diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index e6eaabd9ce..5fdb6a5196 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -645,7 +645,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu } set_items.clear(); input_action = INPUT_NONE; - return true; + return set_items.size() > 0; } if (mb->get_button_index() == BUTTON_LEFT && input_action != INPUT_NONE) { diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index 3398957775..e0cd67a810 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "stream_peer_mbed_tls.h" +#include "core/io/stream_peer_tcp.h" #include "core/os/file_access.h" #include "mbedtls/platform_util.h" @@ -98,12 +99,16 @@ Error StreamPeerMbedTLS::_do_handshake() { int ret = 0; while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + // An error occurred. ERR_PRINTS("TLS handshake error: " + itos(ret)); _print_error(ret); disconnect_from_stream(); status = STATUS_ERROR; return FAILED; - } else if (!blocking_handshake) { + } + + // Handshake is still in progress. + if (!blocking_handshake) { // Will retry via poll later return OK; } @@ -192,7 +197,12 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in int ret = mbedtls_ssl_write(&ssl, p_data, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - ret = 0; // non blocking io + // Non blocking IO + ret = 0; + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // Clean close + disconnect_from_stream(); + return ERR_FILE_EOF; } else if (ret <= 0) { _print_error(ret); disconnect_from_stream(); @@ -234,6 +244,10 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r int ret = mbedtls_ssl_read(&ssl, p_buffer, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = 0; // non blocking io + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // Clean close + disconnect_from_stream(); + return ERR_FILE_EOF; } else if (ret <= 0) { _print_error(ret); disconnect_from_stream(); @@ -256,9 +270,22 @@ void StreamPeerMbedTLS::poll() { int ret = mbedtls_ssl_read(&ssl, NULL, 0); - if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + // Nothing to read/write (non blocking IO) + } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // Clean close (disconnect) + disconnect_from_stream(); + return; + } else if (ret < 0) { _print_error(ret); disconnect_from_stream(); + return; + } + + Ref<StreamPeerTCP> tcp = base; + if (tcp.is_valid() && tcp->get_status() != STATUS_CONNECTED) { + disconnect_from_stream(); + return; } } @@ -282,6 +309,12 @@ void StreamPeerMbedTLS::disconnect_from_stream() { if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING) return; + Ref<StreamPeerTCP> tcp = base; + if (tcp.is_valid() && tcp->get_status() == STATUS_CONNECTED) { + // We are still connected on the socket, try to send close notity. + mbedtls_ssl_close_notify(&ssl); + } + _cleanup(); } @@ -317,15 +350,13 @@ void StreamPeerMbedTLS::initialize_ssl() { mbedtls_debug_set_threshold(1); #endif - PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array(); - - if (cert_array.size() > 0) - _load_certs(cert_array); - available = true; } void StreamPeerMbedTLS::finalize_ssl() { + available = false; + _create = NULL; + load_certs_func = NULL; mbedtls_x509_crt_free(&cacert); } diff --git a/modules/mono/SCsub b/modules/mono/SCsub index b3a2d26e4a..1d5c145027 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -134,6 +134,7 @@ def find_msbuild_unix(filename): def find_msbuild_windows(): import mono_reg_utils as monoreg + mono_root = '' bits = env['bits'] if bits == '32': diff --git a/modules/mono/config.py b/modules/mono/config.py index 70fd1a35f1..01649a972e 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -99,6 +99,8 @@ def configure(env): if not mono_root: raise RuntimeError('Mono installation directory not found') + print('Found Mono root directory: ' + mono_root) + mono_version = mono_root_try_find_mono_version(mono_root) configure_for_mono_version(env, mono_version) @@ -164,6 +166,14 @@ def configure(env): if os.getenv('MONO64_PREFIX'): mono_root = os.getenv('MONO64_PREFIX') + if not mono_root and sys.platform == 'darwin': + # Try with some known directories under OSX + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current', '/usr/local/var/homebrew/linked/mono'] + for hint_dir in hint_dirs: + if os.path.isdir(hint_dir): + mono_root = hint_dir + break + # We can't use pkg-config to link mono statically, # but we can still use it to find the mono root directory if not mono_root and mono_static: @@ -172,6 +182,8 @@ def configure(env): raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') if mono_root: + print('Found Mono root directory: ' + mono_root) + mono_version = mono_root_try_find_mono_version(mono_root) configure_for_mono_version(env, mono_version) @@ -216,6 +228,9 @@ def configure(env): else: assert not mono_static + # TODO: Add option to force using pkg-config + print('Mono root directory not found. Using pkg-config instead') + mono_version = pkgconfig_try_find_mono_version() configure_for_mono_version(env, mono_version) @@ -248,7 +263,7 @@ def configure(env): def configure_for_mono_version(env, mono_version): if mono_version is None: raise RuntimeError('Mono JIT compiler version not found') - print('Mono JIT compiler version: ' + str(mono_version)) + print('Found Mono JIT compiler version: ' + str(mono_version)) if mono_version >= LooseVersion("5.12.0"): env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) @@ -282,7 +297,14 @@ def pkgconfig_try_find_mono_version(): def mono_root_try_find_mono_version(mono_root): from compat import decode_utf8 - output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version']) + mono_bin = os.path.join(mono_root, 'bin') + if os.path.isfile(os.path.join(mono_bin, 'mono')): + mono_binary = os.path.join(mono_bin, 'mono') + elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')): + mono_binary = os.path.join(mono_bin, 'mono.exe') + else: + return None + output = subprocess.check_output([mono_binary, '--version']) first_line = decode_utf8(output.splitlines()[0]) try: return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) diff --git a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs b/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs index 303be3b732..fba4a8f65c 100644 --- a/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs +++ b/modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs @@ -2,13 +2,23 @@ using System; using System.IO; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace GodotSharpTools.Editor { public class MonoDevelopInstance { - private Process process; - private string solutionFile; + public enum EditorId + { + MonoDevelop = 0, + VisualStudioForMac = 1 + } + + readonly string solutionFile; + readonly EditorId editorId; + + Process process; public void Execute(string[] files) { @@ -16,6 +26,35 @@ namespace GodotSharpTools.Editor List<string> args = new List<string>(); + string command; + + if (Utils.OS.IsOSX()) + { + string bundleId = codeEditorBundleIds[editorId]; + + if (IsApplicationBundleInstalled(bundleId)) + { + command = "open"; + + args.Add("-b"); + args.Add(bundleId); + + // The 'open' process must wait until the application finishes + if (newWindow) + args.Add("--wait-apps"); + + args.Add("--args"); + } + else + { + command = codeEditorPaths[editorId]; + } + } + else + { + command = codeEditorPaths[editorId]; + } + args.Add("--ipc-tcp"); if (newWindow) @@ -33,25 +72,73 @@ namespace GodotSharpTools.Editor if (newWindow) { - ProcessStartInfo startInfo = new ProcessStartInfo(MonoDevelopFile, string.Join(" ", args)); - process = Process.Start(startInfo); + process = Process.Start(new ProcessStartInfo() + { + FileName = command, + Arguments = string.Join(" ", args), + UseShellExecute = false + }); } else { - Process.Start(MonoDevelopFile, string.Join(" ", args)); + Process.Start(new ProcessStartInfo() + { + FileName = command, + Arguments = string.Join(" ", args), + UseShellExecute = false + }); } } - public MonoDevelopInstance(string solutionFile) + public MonoDevelopInstance(string solutionFile, EditorId editorId) { + if (editorId == EditorId.VisualStudioForMac && !Utils.OS.IsOSX()) + throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform"); + this.solutionFile = solutionFile; + this.editorId = editorId; } - private static string MonoDevelopFile + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static bool IsApplicationBundleInstalled(string bundleId); + + static readonly IReadOnlyDictionary<EditorId, string> codeEditorPaths; + static readonly IReadOnlyDictionary<EditorId, string> codeEditorBundleIds; + + static MonoDevelopInstance() { - get + if (Utils.OS.IsOSX()) + { + codeEditorPaths = new Dictionary<EditorId, string> + { + // Rely on PATH + { EditorId.MonoDevelop, "monodevelop" }, + { EditorId.VisualStudioForMac, "VisualStudio" } + }; + codeEditorBundleIds = new Dictionary<EditorId, string> + { + // TODO EditorId.MonoDevelop + { EditorId.VisualStudioForMac, "com.microsoft.visual-studio" } + }; + } + else if (Utils.OS.IsWindows()) + { + codeEditorPaths = new Dictionary<EditorId, string> + { + // XamarinStudio is no longer a thing, and the latest version is quite old + // MonoDevelop is available from source only on Windows. The recommendation + // is to use Visual Studio instead. Since there are no official builds, we + // will rely on custom MonoDevelop builds being added to PATH. + { EditorId.MonoDevelop, "MonoDevelop.exe" } + }; + } + else if (Utils.OS.IsUnix()) { - return "monodevelop"; + codeEditorPaths = new Dictionary<EditorId, string> + { + // Rely on PATH + { EditorId.MonoDevelop, "monodevelop" } + }; } } } diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj index 1c8714e31d..773e8196f7 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -40,6 +40,7 @@ <Compile Include="Project\ProjectGenerator.cs" /> <Compile Include="Project\ProjectUtils.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Utils\OS.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs b/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs deleted file mode 100644 index 0cbafdc20d..0000000000 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.userprefs +++ /dev/null @@ -1,14 +0,0 @@ -<Properties StartupItem="GodotSharpTools.csproj"> - <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" /> - <MonoDevelop.Ide.Workbench ActiveDocument="Build/BuildSystem.cs"> - <Files> - <File FileName="Build/ProjectExtensions.cs" Line="1" Column="1" /> - <File FileName="Build/ProjectGenerator.cs" Line="1" Column="1" /> - <File FileName="Build/BuildSystem.cs" Line="37" Column="14" /> - </Files> - </MonoDevelop.Ide.Workbench> - <MonoDevelop.Ide.DebuggingService.Breakpoints> - <BreakpointStore /> - </MonoDevelop.Ide.DebuggingService.Breakpoints> - <MonoDevelop.Ide.DebuggingService.PinnedWatches /> -</Properties>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/Utils/OS.cs b/modules/mono/editor/GodotSharpTools/Utils/OS.cs new file mode 100644 index 0000000000..148e954e77 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/Utils/OS.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace GodotSharpTools.Utils +{ + public static class OS + { + [MethodImpl(MethodImplOptions.InternalCall)] + extern static string GetPlatformName(); + + const string HaikuName = "Haiku"; + const string OSXName = "OSX"; + const string ServerName = "Server"; + const string UWPName = "UWP"; + const string WindowsName = "Windows"; + const string X11Name = "X11"; + + public static bool IsHaiku() + { + return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsOSX() + { + return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsServer() + { + return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsUWP() + { + return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsWindows() + { + return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsX11() + { + return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase); + } + + static bool? IsUnixCache = null; + static readonly string[] UnixPlatforms = new string[] { HaikuName, OSXName, ServerName, X11Name }; + + public static bool IsUnix() + { + if (IsUnixCache.HasValue) + return IsUnixCache.Value; + + string osName = GetPlatformName(); + IsUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase)); + return IsUnixCache.Value; + } + } +} diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index b01f8e66c3..d397814fa7 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -94,7 +94,12 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() { #if defined(WINDOWS_ENABLED) switch (build_tool) { case GodotSharpBuilds::MSBUILD_VS: { - static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); + static String msbuild_tools_path; + + if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); + } if (msbuild_tools_path.length()) { if (!msbuild_tools_path.ends_with("\\")) @@ -128,15 +133,25 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() { CRASH_NOW(); } #elif defined(UNIX_ENABLED) - static String msbuild_path = _find_build_engine_on_unix("msbuild"); - static String xbuild_path = _find_build_engine_on_unix("xbuild"); + static String msbuild_path; + static String xbuild_path; if (build_tool == GodotSharpBuilds::XBUILD) { + if (xbuild_path.empty() || !FileAccess::exists(xbuild_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + xbuild_path = _find_build_engine_on_unix("msbuild"); + } + if (xbuild_path.empty()) { WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'"); return NULL; } } else { + if (msbuild_path.empty() || !FileAccess::exists(msbuild_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + msbuild_path = _find_build_engine_on_unix("msbuild"); + } + if (msbuild_path.empty()) { WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'"); return NULL; @@ -192,7 +207,11 @@ MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() { #endif } -void GodotSharpBuilds::_register_internal_calls() { +void GodotSharpBuilds::register_internal_calls() { + + static bool registered = false; + ERR_FAIL_COND(registered); + registered = true; mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback); mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath); diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index 4afc284d45..c6dc6b6236 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -61,9 +61,6 @@ private: static GodotSharpBuilds *singleton; - friend class GDMono; - static void _register_internal_calls(); - public: enum BuildTool { MSBUILD_MONO, @@ -75,6 +72,8 @@ public: _FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } + static void register_internal_calls(); + static void show_build_error_dialog(const String &p_message); void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index faeb58e5a7..3ee38515bf 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -38,12 +38,17 @@ #include "../csharp_script.h" #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono.h" +#include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "bindings_generator.h" #include "csharp_project.h" #include "godotsharp_export.h" #include "net_solution.h" +#ifdef OSX_ENABLED +#include "../utils/osx_utils.h" +#endif + #ifdef WINDOWS_ENABLED #include "../utils/mono_reg_utils.h" #endif @@ -169,6 +174,31 @@ void GodotSharpEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed); } +MonoBoolean godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled(MonoString *p_bundle_id) { +#ifdef OSX_ENABLED + return (MonoBoolean)osx_is_app_bundle_installed(GDMonoMarshal::mono_string_to_godot(p_bundle_id)); +#else + (void)p_bundle_id; // UNUSED + ERR_FAIL_V(false); +#endif +} + +MonoString *godot_icall_Utils_OS_GetPlatformName() { + return GDMonoMarshal::mono_string_from_godot(OS::get_singleton()->get_name()); +} + +void GodotSharpEditor::register_internal_calls() { + + static bool registered = false; + ERR_FAIL_COND(registered); + registered = true; + + mono_add_internal_call("GodotSharpTools.Editor.MonoDevelopInstance::IsApplicationBundleInstalled", (void *)godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled); + mono_add_internal_call("GodotSharpTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName); + + GodotSharpBuilds::register_internal_calls(); +} + void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) { error_dialog->set_title(p_title); @@ -181,8 +211,36 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))); switch (editor) { - case EDITOR_CODE: { + case EDITOR_VSCODE: { + static String vscode_path; + + if (vscode_path.empty() || !FileAccess::exists(vscode_path)) { + // Try to search it again if it wasn't found last time or if it was removed from its location + vscode_path = path_which("code"); + } + List<String> args; + +#ifdef OSX_ENABLED + // The package path is '/Applications/Visual Studio Code.app' + static const String vscode_bundle_id = "com.microsoft.VSCode"; + static bool osx_app_bundle_installed = osx_is_app_bundle_installed(vscode_bundle_id); + + if (osx_app_bundle_installed) { + args.push_back("-b"); + args.push_back(vscode_bundle_id); + + // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is + // editing our folder. It's better to ask for a new window and let VSCode do the window management. + args.push_back("-n"); + + // The open process must wait until the application finishes (which is instant in VSCode's case) + args.push_back("--wait-apps"); + + args.push_back("--args"); + } +#endif + args.push_back(ProjectSettings::get_singleton()->get_resource_path()); String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); @@ -194,18 +252,47 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int args.push_back(script_path); } - static String program = path_which("code"); +#ifdef OSX_ENABLED + ERR_EXPLAIN("Cannot find code editor: VSCode"); + ERR_FAIL_COND_V(!osx_app_bundle_installed && vscode_path.empty(), ERR_FILE_NOT_FOUND); - Error err = OS::get_singleton()->execute(program.length() ? program : "code", args, false); + String command = osx_app_bundle_installed ? "/usr/bin/open" : vscode_path; +#else + ERR_EXPLAIN("Cannot find code editor: VSCode"); + ERR_FAIL_COND_V(vscode_path.empty(), ERR_FILE_NOT_FOUND); + + String command = vscode_path; +#endif + + Error err = OS::get_singleton()->execute(command, args, false); if (err != OK) { - ERR_PRINT("GodotSharp: Could not execute external editor"); + ERR_PRINT("Error when trying to execute code editor: VSCode"); return err; } } break; +#ifdef OSX_ENABLED + case EDITOR_VISUALSTUDIO_MAC: + // [[fallthrough]]; +#endif case EDITOR_MONODEVELOP: { - if (!monodevel_instance) - monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path())); +#ifdef OSX_ENABLED + bool is_visualstudio = editor == EDITOR_VISUALSTUDIO_MAC; + + MonoDevelopInstance **instance = is_visualstudio ? + &visualstudio_mac_instance : + &monodevelop_instance; + + MonoDevelopInstance::EditorId editor_id = is_visualstudio ? + MonoDevelopInstance::VISUALSTUDIO_FOR_MAC : + MonoDevelopInstance::MONODEVELOP; +#else + MonoDevelopInstance **instance = &monodevelop_instance; + MonoDevelopInstance::EditorId editor_id = MonoDevelopInstance::MONODEVELOP; +#endif + + if (!*instance) + *instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path(), editor_id)); String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); @@ -213,7 +300,7 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int script_path += ";" + itos(p_line + 1) + ";" + itos(p_col); } - monodevel_instance->execute(script_path); + (*instance)->execute(script_path); } break; default: return ERR_UNAVAILABLE; @@ -231,7 +318,10 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { singleton = this; - monodevel_instance = NULL; + monodevelop_instance = NULL; +#ifdef OSX_ENABLED + visualstudio_mac_instance = NULL; +#endif editor = p_editor; @@ -314,7 +404,18 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { // External editor settings EditorSettings *ed_settings = EditorSettings::get_singleton(); EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE); - ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code")); + + String settings_hint_str = "None"; + +#ifdef WINDOWS_ENABLED + settings_hint_str += ",MonoDevelop,Visual Studio Code"; +#elif OSX_ENABLED + settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code"; +#elif UNIX_ENABLED + settings_hint_str += ",MonoDevelop,Visual Studio Code"; +#endif + + ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, settings_hint_str)); // Export plugin Ref<GodotSharpExport> godotsharp_export; @@ -328,9 +429,9 @@ GodotSharpEditor::~GodotSharpEditor() { memdelete(godotsharp_builds); - if (monodevel_instance) { - memdelete(monodevel_instance); - monodevel_instance = NULL; + if (monodevelop_instance) { + memdelete(monodevelop_instance); + monodevelop_instance = NULL; } } diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index 66da814c8b..46b6bd5ebf 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -50,7 +50,10 @@ class GodotSharpEditor : public Node { GodotSharpBuilds *godotsharp_builds; - MonoDevelopInstance *monodevel_instance; + MonoDevelopInstance *monodevelop_instance; +#ifdef OSX_ENABLED + MonoDevelopInstance *visualstudio_mac_instance; +#endif bool _create_project_solution(); @@ -74,12 +77,24 @@ public: enum ExternalEditor { EDITOR_NONE, +#ifdef WINDOWS_ENABLED + //EDITOR_VISUALSTUDIO, // TODO EDITOR_MONODEVELOP, - EDITOR_CODE, + EDITOR_VSCODE +#elif OSX_ENABLED + EDITOR_VISUALSTUDIO_MAC, + EDITOR_MONODEVELOP, + EDITOR_VSCODE +#elif UNIX_ENABLED + EDITOR_MONODEVELOP, + EDITOR_VSCODE +#endif }; _FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; } + static void register_internal_calls(); + void show_error_dialog(const String &p_message, const String &p_title = "Error"); Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col); diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 9f05711fd6..1d858d80bf 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -47,7 +47,7 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) { execute_method->invoke(gc_handle->get_target(), args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL(); } } @@ -59,7 +59,7 @@ void MonoDevelopInstance::execute(const String &p_file) { execute(files); } -MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { +MonoDevelopInstance::MonoDevelopInstance(const String &p_solution, EditorId p_editor_id) { _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) @@ -67,15 +67,16 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); - GDMonoMethod *ctor = klass->get_method(".ctor", 1); + GDMonoMethod *ctor = klass->get_method(".ctor", 2); MonoException *exc = NULL; Variant solution = p_solution; - const Variant *args[1] = { &solution }; + Variant editor_id = p_editor_id; + const Variant *args[2] = { &solution, &editor_id }; ctor->invoke(obj, args, &exc); if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); ERR_FAIL(); } diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h index 73cf0f54cc..29de4a4735 100644 --- a/modules/mono/editor/monodevelop_instance.h +++ b/modules/mono/editor/monodevelop_instance.h @@ -42,10 +42,15 @@ class MonoDevelopInstance { GDMonoMethod *execute_method; public: + enum EditorId { + MONODEVELOP = 0, + VISUALSTUDIO_FOR_MAC = 1 + }; + void execute(const Vector<String> &p_files); void execute(const String &p_file); - MonoDevelopInstance(const String &p_solution); + MonoDevelopInstance(const String &p_solution, EditorId p_editor_id); }; #endif // MONODEVELOP_INSTANCE_H diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index ec96a9e2fa..a5618cb28d 100644 --- a/modules/mono/glue/Managed/Files/Basis.cs +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -165,6 +165,38 @@ namespace Godot ); } + internal Quat RotationQuat() + { + Basis orthonormalizedBasis = Orthonormalized(); + real_t det = orthonormalizedBasis.Determinant(); + if (det < 0) + { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + orthonormalizedBasis = orthonormalizedBasis.Scaled(Vector3.NegOne); + } + + return orthonormalizedBasis.Quat(); + } + + internal void SetQuantScale(Quat quat, Vector3 scale) + { + SetDiagonal(scale); + Rotate(quat); + } + + private void Rotate(Quat quat) + { + this *= new Basis(quat); + } + + private void SetDiagonal(Vector3 diagonal) + { + _x = new Vector3(diagonal.x, 0, 0); + _y = new Vector3(0, diagonal.y, 0); + _z = new Vector3(0, 0, diagonal.z); + + } + public real_t Determinant() { return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index e432d5b52c..068007d7f0 100644 --- a/modules/mono/glue/Managed/Files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -20,6 +20,25 @@ namespace Godot return new Transform(basisInv, basisInv.Xform(-origin)); } + public Transform InterpolateWith(Transform transform, real_t c) + { + /* not sure if very "efficient" but good enough? */ + + Vector3 sourceScale = basis.Scale; + Quat sourceRotation = basis.RotationQuat(); + Vector3 sourceLocation = origin; + + Vector3 destinationScale = transform.basis.Scale; + Quat destinationRotation = transform.basis.RotationQuat(); + Vector3 destinationLocation = transform.origin; + + var interpolated = new Transform(); + interpolated.basis.SetQuantScale(sourceRotation.Slerp(destinationRotation, c).Normalized(), sourceScale.LinearInterpolate(destinationScale, c)); + interpolated.origin = sourceLocation.LinearInterpolate(destinationLocation, c); + + return interpolated; + } + public Transform Inverse() { Basis basisTr = basis.Transposed(); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 03418a02ea..2fed6064b7 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -319,7 +319,7 @@ void GDMono::_register_internal_calls() { #endif #ifdef TOOLS_ENABLED - GodotSharpBuilds::_register_internal_calls(); + GodotSharpEditor::register_internal_calls(); #endif } diff --git a/editor/editor_initialize_ssl.cpp b/modules/mono/utils/osx_utils.cpp index 9f7537cc9a..f520706a0c 100644 --- a/editor/editor_initialize_ssl.cpp +++ b/modules/mono/utils/osx_utils.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* editor_initialize_ssl.cpp */ +/* osx_utils.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,21 +28,35 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "editor_initialize_ssl.h" +#include "osx_utils.h" -#include "certs_compressed.gen.h" -#include "core/io/compression.h" -#include "core/io/stream_peer_ssl.h" +#include "core/print_string.h" -void editor_initialize_certificates() { +#ifdef OSX_ENABLED - PoolByteArray data; - data.resize(_certs_uncompressed_size + 1); - { - PoolByteArray::Write w = data.write(); - Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); - w[_certs_uncompressed_size] = 0; //make sure it ends at zero +#include <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> + +bool osx_is_app_bundle_installed(const String &p_bundle_id) { + + CFURLRef app_url = NULL; + CFStringRef bundle_id = CFStringCreateWithCString(NULL, p_bundle_id.utf8(), kCFStringEncodingUTF8); + OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, NULL, NULL, &app_url); + CFRelease(bundle_id); + + if (app_url) + CFRelease(app_url); + + switch (result) { + case noErr: + return true; + case kLSApplicationNotFoundErr: + break; + default: + break; } - StreamPeerSSL::load_certs_from_memory(data); + return false; } + +#endif diff --git a/editor/editor_initialize_ssl.h b/modules/mono/utils/osx_utils.h index 71d16b8c53..68479b4f44 100644 --- a/editor/editor_initialize_ssl.h +++ b/modules/mono/utils/osx_utils.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* editor_initialize_ssl.h */ +/* osx_utils.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,9 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef EDITOR_INITIALIZE_SSL_H -#define EDITOR_INITIALIZE_SSL_H +#include "core/ustring.h" -void editor_initialize_certificates(); +#ifndef OSX_UTILS_H -#endif // EDITOR_INITIALIZE_SSL_H +#ifdef OSX_ENABLED + +bool osx_is_app_bundle_installed(const String &p_bundle_id); + +#endif + +#endif // OSX_UTILS_H diff --git a/modules/opensimplex/doc_classes/SimplexNoise.xml b/modules/opensimplex/doc_classes/SimplexNoise.xml index a5a01d88a7..e5e0c15b3c 100644 --- a/modules/opensimplex/doc_classes/SimplexNoise.xml +++ b/modules/opensimplex/doc_classes/SimplexNoise.xml @@ -4,9 +4,7 @@ Noise generator based on Open Simplex. </brief_description> <description> - This resource allows you to configure and sample a fractal noise space. - - Here is a brief usage example that configures a SimplexNoise and gets samples at various positions and dimensions: + This resource allows you to configure and sample a fractal noise space. Here is a brief usage example that configures a SimplexNoise and gets samples at various positions and dimensions: [codeblock] var noise = SimplexNoise.new() @@ -14,13 +12,13 @@ noise.seed = randi() noise.octaves = 4 noise.period = 20.0 - noise.persistance = 0.8 - - #Sample + noise.persistence = 0.8 + + # Sample print("Values:") - print(noise.get_noise_2d(1.0,1.0)) - print(noise.get_noise_3d(0.5,3.0,15.0)) - print(noise.get_noise_3d(0.5,1.9,4.7,0.0)) + print(noise.get_noise_2d(1.0, 1.0)) + print(noise.get_noise_3d(0.5, 3.0, 15.0)) + print(noise.get_noise_4d(0.5, 1.9, 4.7, 0.0)) [/codeblock] </description> <tutorials> @@ -47,7 +45,7 @@ <argument index="1" name="y" type="float"> </argument> <description> - 2D noise value [-1,1] at position [code]x[/code],[code]y[/code]. + Returns the 2D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_2dv"> @@ -56,7 +54,7 @@ <argument index="0" name="pos" type="Vector2"> </argument> <description> - 2D noise value [-1,1] at position [code]pos.x[/code],[code]pos.y[/code]. + Returns the 2D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_3d"> @@ -69,7 +67,7 @@ <argument index="2" name="z" type="float"> </argument> <description> - 3D noise value [-1,1] at position [code]x[/code],[code]y[/code],[code]z[/code]. + Returns the 3D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_3dv"> @@ -78,7 +76,7 @@ <argument index="0" name="pos" type="Vector3"> </argument> <description> - 3D noise value [-1,1] at position [code]pos.x[/code],[code]pos.y[/code],[code]pos.z[/code]. + Returns the 3D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_4d"> @@ -93,7 +91,7 @@ <argument index="3" name="w" type="float"> </argument> <description> - 4D noise value [-1,1] at position [code]x[/code],[code]y[/code],[code]z[/code],[code]w[/code]. + Returns the 4D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_seamless_image"> @@ -102,8 +100,7 @@ <argument index="0" name="size" type="int"> </argument> <description> - Generate a tileable noise image, based on the current noise parameters. - Generated seamless images are always square ([code]size[/code]x[code]size[/code]). + Generate a tileable noise image, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] x [code]size[/code]). </description> </method> </methods> @@ -112,15 +109,13 @@ Difference in period between [member octaves]. </member> <member name="octaves" type="int" setter="set_octaves" getter="get_octaves"> - Number of Simplex Noise layers that are sampled to get the fractal noise. + Number of Simplex noise layers that are sampled to get the fractal noise. </member> <member name="period" type="float" setter="set_period" getter="get_period"> - Period of the base octave. - A lower period results in a higher frequancy noise (more value changes across the same distance). + Period of the base octave. A lower period results in a higher-frequency noise (more value changes across the same distance). </member> - <member name="persistance" type="float" setter="set_persistance" getter="get_persistance"> - Contribuiton factor of the different octaves. - A [code]persistance[/code] value of 1 means all the octaves have the same contribution, a value of 0.5 means each octave contributes half as much as the previous one. + <member name="persistence" type="float" setter="set_persistence" getter="get_persistence"> + Contribution factor of the different octaves. A [code]persistence[/code] value of 1 means all the octaves have the same contribution, a value of 0.5 means each octave contributes half as much as the previous one. </member> <member name="seed" type="int" setter="set_seed" getter="get_seed"> Seed used to generate random values, different seeds will generate different noise maps. diff --git a/modules/opensimplex/simplex_noise.cpp b/modules/opensimplex/simplex_noise.cpp index 6d66c7110e..e489b7f6f0 100644 --- a/modules/opensimplex/simplex_noise.cpp +++ b/modules/opensimplex/simplex_noise.cpp @@ -35,7 +35,7 @@ SimplexNoise::SimplexNoise() { seed = 0; - persistance = 0.5; + persistence = 0.5; octaves = 3; period = 64; lacunarity = 2.0; @@ -81,9 +81,9 @@ void SimplexNoise::set_period(float p_period) { emit_changed(); } -void SimplexNoise::set_persistance(float p_persistance) { - if (p_persistance == persistance) return; - persistance = p_persistance; +void SimplexNoise::set_persistence(float p_persistence) { + if (p_persistence == persistence) return; + persistence = p_persistence; emit_changed(); } @@ -164,8 +164,8 @@ void SimplexNoise::_bind_methods() { ClassDB::bind_method(D_METHOD("set_period", "period"), &SimplexNoise::set_period); ClassDB::bind_method(D_METHOD("get_period"), &SimplexNoise::get_period); - ClassDB::bind_method(D_METHOD("set_persistance", "persistance"), &SimplexNoise::set_persistance); - ClassDB::bind_method(D_METHOD("get_persistance"), &SimplexNoise::get_persistance); + ClassDB::bind_method(D_METHOD("set_persistence", "persistence"), &SimplexNoise::set_persistence); + ClassDB::bind_method(D_METHOD("get_persistence"), &SimplexNoise::get_persistence); ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &SimplexNoise::set_lacunarity); ClassDB::bind_method(D_METHOD("get_lacunarity"), &SimplexNoise::get_lacunarity); @@ -183,7 +183,7 @@ void SimplexNoise::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, "1,6,1"), "set_octaves", "get_octaves"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "period", PROPERTY_HINT_RANGE, "0.1,256.0,0.1"), "set_period", "get_period"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "persistance", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_persistance", "get_persistance"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "persistence", PROPERTY_HINT_RANGE, "0.0,1.0,0.001"), "set_persistence", "get_persistence"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); } @@ -200,7 +200,7 @@ float SimplexNoise::get_noise_2d(float x, float y) { while (++i < octaves) { x *= lacunarity; y *= lacunarity; - amp *= persistance; + amp *= persistence; max += amp; sum += _get_octave_noise_2d(i, x, y) * amp; } @@ -223,7 +223,7 @@ float SimplexNoise::get_noise_3d(float x, float y, float z) { x *= lacunarity; y *= lacunarity; z *= lacunarity; - amp *= persistance; + amp *= persistence; max += amp; sum += _get_octave_noise_3d(i, x, y, z) * amp; } @@ -248,7 +248,7 @@ float SimplexNoise::get_noise_4d(float x, float y, float z, float w) { y *= lacunarity; z *= lacunarity; w *= lacunarity; - amp *= persistance; + amp *= persistence; max += amp; sum += _get_octave_noise_4d(i, x, y, z, w) * amp; } diff --git a/modules/opensimplex/simplex_noise.h b/modules/opensimplex/simplex_noise.h index 59390c6172..9a48dbf809 100644 --- a/modules/opensimplex/simplex_noise.h +++ b/modules/opensimplex/simplex_noise.h @@ -44,7 +44,7 @@ class SimplexNoise : public Resource { osn_context contexts[6]; int seed; - float persistance; // Controls details, value in [0,1]. Higher increases grain, lower increases smoothness. + float persistence; // Controls details, value in [0,1]. Higher increases grain, lower increases smoothness. int octaves; // Number of noise layers float period; // Distance above which we start to see similarities. The higher, the longer "hills" will be on a terrain. float lacunarity; // Controls period change across octaves. 2 is usually a good value to address all detail levels. @@ -64,8 +64,8 @@ public: void set_period(float p_period); float get_period() const { return period; } - void set_persistance(float p_persistance); - float get_persistance() const { return persistance; } + void set_persistence(float p_persistence); + float get_persistence() const { return persistence; } void set_lacunarity(float p_lacunarity); float get_lacunarity() const { return lacunarity; } diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index e7a4e0c31f..60bc54afe4 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -260,7 +260,12 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_SQRT: { return PropertyInfo(Variant::REAL, "s"); } break; - case MATH_ATAN2: + case MATH_ATAN2: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "y"); + else + return PropertyInfo(Variant::REAL, "x"); + } break; case MATH_FMOD: case MATH_FPOSMOD: { if (p_idx == 0) diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 3515eeeb60..b0ec3c4245 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -39,6 +39,7 @@ #include "unix/file_access_unix.h" #include <emscripten.h> +#include <png.h> #include <stdlib.h> #include "dom_keys.inc" @@ -912,6 +913,57 @@ void OS_JavaScript::set_window_title(const String &p_title) { /* clang-format on */ } +void OS_JavaScript::set_icon(const Ref<Image> &p_icon) { + + ERR_FAIL_COND(p_icon.is_null()); + Ref<Image> icon = p_icon; + if (icon->is_compressed()) { + icon = icon->duplicate(); + ERR_FAIL_COND(icon->decompress() != OK) + } + if (icon->get_format() != Image::FORMAT_RGBA8) { + if (icon == p_icon) + icon = icon->duplicate(); + icon->convert(Image::FORMAT_RGBA8); + } + + png_image png_meta; + memset(&png_meta, 0, sizeof png_meta); + png_meta.version = PNG_IMAGE_VERSION; + png_meta.width = icon->get_width(); + png_meta.height = icon->get_height(); + png_meta.format = PNG_FORMAT_RGBA; + + PoolByteArray png; + size_t len; + PoolByteArray::Read r = icon->get_data().read(); + ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, r.ptr(), 0, NULL)); + + png.resize(len); + PoolByteArray::Write w = png.write(); + ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, w.ptr(), &len, 0, r.ptr(), 0, NULL)); + w = PoolByteArray::Write(); + + r = png.read(); + /* clang-format off */ + EM_ASM_ARGS({ + var PNG_PTR = $0; + var PNG_LEN = $1; + + var png = new Blob([HEAPU8.slice(PNG_PTR, PNG_PTR + PNG_LEN)], { type: "image/png" }); + var url = URL.createObjectURL(png); + var link = document.getElementById('-gd-engine-icon'); + if (link === null) { + link = document.createElement('link'); + link.rel = 'icon'; + link.id = '-gd-engine-icon'; + document.head.appendChild(link); + } + link.href = url; + }, r.ptr(), len); + /* clang-format on */ +} + String OS_JavaScript::get_executable_path() const { return OS::get_executable_path(); diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index f40fb8fc7e..ddcbf8c7c9 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -135,6 +135,7 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual void set_window_title(const String &p_title); + virtual void set_icon(const Ref<Image> &p_icon); String get_executable_path() const; virtual Error shell_open(String p_uri); virtual String get_name(); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index b98113baeb..886ff4b332 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -1663,7 +1663,9 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c cursors[p_shape] = cursor; if (p_shape == cursor_shape) { - [cursor set]; + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + [cursor set]; + } } [imgrep release]; diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 586533e817..5dfb1592e0 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -29,6 +29,7 @@ prog = env.add_program('#bin/godot', common_win + res_obj, PROGSUFFIX=env["PROGS # Microsoft Visual Studio Project Generation if env['vsproj']: env.vs_srcs = env.vs_srcs + ["platform/windows/" + res_file] + env.vs_srcs = env.vs_srcs + ["platform/windows/godot.natvis"] for x in common_win: env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)] diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis new file mode 100644 index 0000000000..01963035a1 --- /dev/null +++ b/platform/windows/godot.natvis @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + <Type Name="Vector<*>"> + <Expand> + <Item Name="size">(_cowdata && _cowdata->_ptr) ? (((const unsigned int *)(_cowdata->_ptr))[-1]) : 0</Item> + <ArrayItems> + <Size>(_cowdata && _cowdata->_ptr) ? (((const unsigned int *)(_cowdata->_ptr))[-1]) : 0</Size> + <ValuePointer>(_cowdata) ? (_cowdata->_ptr) : 0</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="PoolVector<*>"> + <Expand> + <Item Name="size">alloc ? (alloc->size / sizeof($T1)) : 0</Item> + <ArrayItems> + <Size>alloc ? (alloc->size / sizeof($T1)) : 0</Size> + <ValuePointer>alloc ? (($T1 *)alloc->mem) : 0</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="Variant"> + <DisplayString Condition="this->type == Variant::NIL">nil</DisplayString> + <DisplayString Condition="this->type == Variant::BOOL">{_data._bool}</DisplayString> + <DisplayString Condition="this->type == Variant::INT">{_data._int}</DisplayString> + <DisplayString Condition="this->type == Variant::REAL">{_data._real}</DisplayString> + <DisplayString Condition="this->type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString> + <DisplayString Condition="this->type == Variant::AABB">{_data._aabb}</DisplayString> + <DisplayString Condition="this->type == Variant::BASIS">{_data._basis}</DisplayString> + <DisplayString Condition="this->type == Variant::TRANSFORM">{_data._transform}</DisplayString> + <DisplayString Condition="this->type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::STRING && ((String *)(&_data._mem[0]))->_cowdata._ptr == 0">""</DisplayString> + <DisplayString Condition="this->type == Variant::STRING && ((String *)(&_data._mem[0]))->_cowdata._ptr != 0">{((String *)(&_data._mem[0]))->_cowdata._ptr,su}</DisplayString> + <DisplayString Condition="this->type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::QUAT">{*(Quat *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::_RID">{*(RID *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_BYTE_ARRAY">{*(PoolByteArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_INT_ARRAY">{*(PoolIntArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_REAL_ARRAY">{*(PoolRealArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_STRING_ARRAY">{*(PoolStringArray *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_VECTOR2_ARRAY">{*(PoolVector2Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_VECTOR3_ARRAY">{*(PoolVector3Array *)_data._mem}</DisplayString> + <DisplayString Condition="this->type == Variant::POOL_COLOR_ARRAY">{*(PoolColorArray *)_data._mem}</DisplayString> + + <StringView Condition="this->type == Variant::STRING && ((String *)(&_data._mem[0]))->_cowdata._ptr != 0">((String *)(&_data._mem[0]))->_cowdata._ptr,su</StringView> + + <Expand> + <Item Name="value" Condition="this->type == Variant::BOOL">_data._bool</Item> + <Item Name="value" Condition="this->type == Variant::INT">_data._int</Item> + <Item Name="value" Condition="this->type == Variant::REAL">_data._real</Item> + <Item Name="value" Condition="this->type == Variant::TRANSFORM2D">_data._transform2d</Item> + <Item Name="value" Condition="this->type == Variant::AABB">_data._aabb</Item> + <Item Name="value" Condition="this->type == Variant::BASIS">_data._basis</Item> + <Item Name="value" Condition="this->type == Variant::TRANSFORM">_data._transform</Item> + <Item Name="value" Condition="this->type == Variant::ARRAY">*(Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::STRING">*(String *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::VECTOR2">*(Vector2 *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::RECT2">*(Rect2 *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::VECTOR3">*(Vector3 *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::PLANE">*(Plane *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::QUAT">*(Quat *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::COLOR">*(Color *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::_RID">*(RID *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::OBJECT">*(Object *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::ARRAY">*(Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_BYTE_ARRAY">*(PoolByteArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_INT_ARRAY">*(PoolIntArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_REAL_ARRAY">*(PoolRealArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_STRING_ARRAY">*(PoolStringArray *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_VECTOR2_ARRAY">*(PoolVector2Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_VECTOR3_ARRAY">*(PoolVector3Array *)_data._mem</Item> + <Item Name="value" Condition="this->type == Variant::POOL_COLOR_ARRAY">*(PoolColorArray *)_data._mem</Item> + </Expand> + </Type> + + <Type Name="String"> + <DisplayString Condition="this->_cowdata._ptr == 0">empty</DisplayString> + <DisplayString Condition="this->_cowdata._ptr != 0">{this->_cowdata._ptr,su}</DisplayString> + <StringView Condition="this->_cowdata._ptr != 0">this->_cowdata._ptr,su</StringView> + </Type> + + <Type Name="Vector2"> + <DisplayString>{{{x},{y}}}</DisplayString> + <Expand> + <Item Name="x">x</Item> + <Item Name="y">y</Item> + </Expand> + </Type> + + <Type Name="Vector3"> + <DisplayString>{{{x},{y},{z}}}</DisplayString> + <Expand> + <Item Name="x">x</Item> + <Item Name="y">y</Item> + <Item Name="z">z</Item> + </Expand> + </Type> + + <Type Name="Quat"> + <DisplayString>Quat {{{x},{y},{z},{w}}}</DisplayString> + <Expand> + <Item Name="x">x</Item> + <Item Name="y">y</Item> + <Item Name="z">z</Item> + <Item Name="w">w</Item> + </Expand> + </Type> + + <Type Name="Color"> + <DisplayString>Color {{{r},{g},{b},{a}}}</DisplayString> + <Expand> + <Item Name="red">r</Item> + <Item Name="green">g</Item> + <Item Name="blue">b</Item> + <Item Name="alpha">a</Item> + </Expand> + </Type> +</AutoVisualizer> diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index f63aebbbd3..d575525f93 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2312,7 +2312,9 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap cursors[p_shape] = CreateIconIndirect(&iconinfo); if (p_shape == cursor_shape) { - SetCursor(cursors[p_shape]); + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + SetCursor(cursors[p_shape]); + } } if (hAndMask != NULL) { diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 24dfe541f9..3d05a650da 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2524,13 +2524,16 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - if (p_shape == current_cursor) + if (p_shape == current_cursor) { return; - if (mouse_mode == MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { - if (cursors[p_shape] != None) + } + + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (cursors[p_shape] != None) { XDefineCursor(x11_display, x11_window, cursors[p_shape]); - else if (cursors[CURSOR_ARROW] != None) + } else if (cursors[CURSOR_ARROW] != None) { XDefineCursor(x11_display, x11_window, cursors[CURSOR_ARROW]); + } } current_cursor = p_shape; @@ -2607,7 +2610,9 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image); if (p_shape == current_cursor) { - XDefineCursor(x11_display, x11_window, cursors[p_shape]); + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + XDefineCursor(x11_display, x11_window, cursors[p_shape]); + } } memfree(cursor_image->pixels); diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp index 9eec8e6cc3..1b789bab9d 100644 --- a/scene/2d/navigation2d.cpp +++ b/scene/2d/navigation2d.cpp @@ -388,10 +388,34 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect Polygon *p = E->get(); float cost = p->distance; - cost += p->center.distance_to(end_point); - if (cost < least_cost) { +#ifdef USE_ENTRY_POINT + int es = p->edges.size(); + float shortest_distance = 1e30; + + for (int i = 0; i < es; i++) { + Polygon::Edge &e = p->edges.write[i]; + + if (!e.C) + continue; + + Vector2 edge[2] = { + _get_vertex(p->edges[i].point), + _get_vertex(p->edges[(i + 1) % es].point) + }; + + Vector2 edge_point = Geometry::get_closest_point_to_segment_2d(p->entry, edge); + float dist = p->entry.distance_to(edge_point); + if (dist < shortest_distance) + shortest_distance = dist; + } + + cost += shortest_distance; +#else + cost += p->center.distance_to(end_point); +#endif + if (cost < least_cost) { least_cost_poly = E; least_cost = cost; } diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 8d84d2408c..54f74c2df3 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -30,6 +30,8 @@ #include "navigation.h" +#define USE_ENTRY_POINT + void Navigation::_navmesh_link(int p_id) { ERR_FAIL_COND(!navmesh_map.has(p_id)); @@ -331,7 +333,18 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector if (begin_poly->edges[i].C) { begin_poly->edges[i].C->prev_edge = begin_poly->edges[i].C_edge; +#ifdef USE_ENTRY_POINT + Vector3 edge[2] = { + _get_vertex(begin_poly->edges[i].point), + _get_vertex(begin_poly->edges[(i + 1) % begin_poly->edges.size()].point) + }; + + Vector3 entry = Geometry::get_closest_point_to_segment(begin_poly->entry, edge); + begin_poly->edges[i].C->distance = begin_poly->entry.distance_to(entry); + begin_poly->edges[i].C->entry = entry; +#else begin_poly->edges[i].C->distance = begin_poly->center.distance_to(begin_poly->edges[i].C->center); +#endif open_list.push_back(begin_poly->edges[i].C); if (begin_poly->edges[i].C == end_poly) { @@ -356,10 +369,33 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector Polygon *p = E->get(); float cost = p->distance; - cost += p->center.distance_to(end_point); +#ifdef USE_ENTRY_POINT + int es = p->edges.size(); - if (cost < least_cost) { + float shortest_distance = 1e30; + + for (int i = 0; i < es; i++) { + Polygon::Edge &e = p->edges.write[i]; + + if (!e.C) + continue; + Vector3 edge[2] = { + _get_vertex(p->edges[i].point), + _get_vertex(p->edges[(i + 1) % es].point) + }; + + Vector3 edge_point = Geometry::get_closest_point_to_segment(p->entry, edge); + float dist = p->entry.distance_to(edge_point); + if (dist < shortest_distance) + shortest_distance = dist; + } + + cost += shortest_distance; +#else + cost += p->center.distance_to(end_point); +#endif + if (cost < least_cost) { least_cost_poly = E; least_cost = cost; } diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h index 5a501039c8..8f200997cd 100644 --- a/scene/3d/navigation.h +++ b/scene/3d/navigation.h @@ -94,6 +94,7 @@ class Navigation : public Spatial { Vector<Edge> edges; Vector3 center; + Vector3 entry; float distance; int prev_edge; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 5f162a3652..283d66d8de 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -349,7 +349,7 @@ void FileDialog::_tree_selected() { file->set_text(d["name"]); } else if (mode == MODE_OPEN_DIR) { - get_ok()->set_text(RTR("Select this Folder")); + get_ok()->set_text(RTR("Select This Folder")); } get_ok()->set_disabled(_is_open_should_be_disabled()); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index ce8de38b74..91dab27930 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -295,14 +295,13 @@ Size2 Label::get_minimum_size() const { Size2 min_style = get_stylebox("normal")->get_minimum_size(); + // don't want to mutable everything + if (word_cache_dirty) + const_cast<Label *>(this)->regenerate_word_cache(); + if (autowrap) return Size2(1, clip ? 1 : minsize.height) + min_style; else { - - // don't want to mutable everything - if (word_cache_dirty) - const_cast<Label *>(this)->regenerate_word_cache(); - Size2 ms = minsize; if (clip) ms.width = 1; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 365a6a5cae..24b9083964 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2239,13 +2239,13 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { _go_left(); } - } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_up") && p_event->is_pressed() && !k->get_command()) { if (!cursor_can_exit_tree) accept_event(); _go_up(); - } else if (p_event->is_action("ui_down") && p_event->is_pressed()) { + } else if (p_event->is_action("ui_down") && p_event->is_pressed() && !k->get_command()) { if (!cursor_can_exit_tree) accept_event(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 12a71fe158..d3282c6ada 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2129,6 +2129,12 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p node->set(name, value); } + List<GroupInfo> groups; + get_groups(&groups); + + for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) + node->add_to_group(E->get().name, E->get().persistent); + node->set_name(get_name()); p_new_parent->add_child(node); @@ -2223,6 +2229,12 @@ Node *Node::duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const { node->set(name, get(name)); } + List<GroupInfo> groups; + get_groups(&groups); + + for (List<GroupInfo>::Element *E = groups.front(); E; E = E->next()) + node->add_to_group(E->get().name, E->get().persistent); + for (int i = 0; i < get_child_count(); i++) { get_child(i)->_duplicate_and_reown(node, p_reown_map); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 50bf8f38f7..b78b3a6ffb 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -178,6 +178,7 @@ PoolVector<int> BitmapFont::_get_kernings() const { void BitmapFont::_set_textures(const Vector<Variant> &p_textures) { + textures.clear(); for (int i = 0; i < p_textures.size(); i++) { Ref<Texture> tex = p_textures[i]; ERR_CONTINUE(!tex.is_valid()); diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 1bfc41bd92..66bf3b4991 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -126,6 +126,11 @@ void Shader::get_default_texture_param_list(List<StringName> *r_textures) const r_textures->push_back(E->key()); } } + +bool Shader::is_text_shader() const { + return true; +} + bool Shader::has_param(const StringName &p_param) const { return params_cache.has(p_param); @@ -235,8 +240,10 @@ Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resourc void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - if (Object::cast_to<Shader>(*p_resource)) { - p_extensions->push_back("shader"); + if (const Shader *shader = Object::cast_to<Shader>(*p_resource)) { + if (shader->is_text_shader()) { + p_extensions->push_back("shader"); + } } } bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 6c91205c0c..c2c205237f 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -79,6 +79,8 @@ public: Ref<Texture> get_default_texture_param(const StringName &p_param) const; void get_default_texture_param_list(List<StringName> *r_textures) const; + virtual bool is_text_shader() const; + _FORCE_INLINE_ StringName remap_param(const StringName &p_param) const { if (params_cache_dirty) get_param_list(NULL); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 6bfb6ec5bf..f12ea8f2bb 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -414,6 +414,10 @@ Shader::Mode VisualShader::get_mode() const { return shader_mode; } +bool VisualShader::is_text_shader() const { + return false; +} + String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const { Ref<VisualShaderNode> node = get_node(p_type, p_node); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 70d2425304..2867daac3a 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -141,6 +141,8 @@ public: void set_mode(Mode p_mode); virtual Mode get_mode() const; + virtual bool is_text_shader() const; + void set_graph_offset(const Vector2 &p_offset); Vector2 get_graph_offset() const; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 35236b23f1..417617fa5b 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -1622,33 +1622,51 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID } }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID } }, { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID } }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID } }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID } }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID } }, { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID } }, { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID } }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID } }, { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID } }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID } }, { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID } }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID } }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID } }, { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID } }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID } }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID } }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID } }, { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID } }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID } }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID } }, { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID } }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID } }, { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, @@ -3437,8 +3455,9 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha } } } else { + ConstantNode::Value value = values[0]; for (int i = 1; i < cardinality; i++) { - values.push_back(values[0]); + values.push_back(value); } } } else if (values.size() != cardinality) { diff --git a/thirdparty/miniupnpc/minissdpc.c b/thirdparty/miniupnpc/minissdpc.c index d76b242ad0..1d29b4ba5b 100644 --- a/thirdparty/miniupnpc/minissdpc.c +++ b/thirdparty/miniupnpc/minissdpc.c @@ -494,7 +494,6 @@ ssdpDiscoverDevices(const char * const deviceTypes[], struct addrinfo hints, *servinfo, *p; #endif #ifdef _WIN32 - MIB_IPFORWARDROW ip_forward; unsigned long _ttl = (unsigned long)ttl; #endif int linklocal = 1; @@ -538,61 +537,103 @@ ssdpDiscoverDevices(const char * const deviceTypes[], * SSDP multicast traffic */ /* Get IP associated with the index given in the ip_forward struct * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */ - if(!ipv6 - && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) { - DWORD dwRetVal = 0; - PMIB_IPADDRTABLE pIPAddrTable; - DWORD dwSize = 0; -#ifdef DEBUG - IN_ADDR IPAddr; -#endif - int i; -#ifdef DEBUG - printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop); -#endif - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE)); - if(pIPAddrTable) { - if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { - free(pIPAddrTable); - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); - } - } - if(pIPAddrTable) { - dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); + if(!ipv6) { + DWORD ifbestidx; + SOCKADDR_IN destAddr; + memset(&destAddr, 0, sizeof(destAddr)); + destAddr.sin_family = AF_INET; + destAddr.sin_addr.s_addr = inet_addr("223.255.255.255"); + destAddr.sin_port = 0; + if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) { + DWORD dwSize = 0; + DWORD dwRetVal = 0; + unsigned int i = 0; + ULONG flags = GAA_FLAG_INCLUDE_PREFIX; + ULONG family = AF_INET; + LPVOID lpMsgBuf = NULL; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + ULONG outBufLen = 0; + ULONG Iterations = 0; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL; + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL; + IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL; + IP_ADAPTER_PREFIX *pPrefix = NULL; + + outBufLen = 15360; + do { + pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen); + if (pAddresses == NULL) { + break; + } + + dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } else { + break; + } + Iterations++; + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 3)); + if (dwRetVal == NO_ERROR) { + pCurrAddresses = pAddresses; + while (pCurrAddresses) { #ifdef DEBUG - printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries); -#endif - for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) { -#ifdef DEBUG - printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr; - printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask; - printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr; - printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr); - printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize); - printf("\tType and State[%d]:", i); + printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex); + printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName); + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++) { + IPAddr.S_un.S_addr = (u_long) pUnicast->Address; + printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); + pUnicast = pUnicast->Next; + } + printf("\tNumber of Unicast Addresses: %d\n", i); + } + pAnycast = pCurrAddresses->FirstAnycastAddress; + if (pAnycast) { + for (i = 0; pAnycast != NULL; i++) { + IPAddr.S_un.S_addr = (u_long) pAnyCast->Address; + printf("\tAnycast Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); + pAnycast = pAnycast->Next; + } + printf("\tNumber of Anycast Addresses: %d\n", i); + } + pMulticast = pCurrAddresses->FirstMulticastAddress; + if (pMulticast) { + for (i = 0; pMulticast != NULL; i++) { + IPAddr.S_un.S_addr = (u_long) pMultiCast->Address; + printf("\tMulticast Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); + } + } printf("\n"); #endif - if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) { + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) { + SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr); /* Set the address of this interface to be used */ struct in_addr mc_if; memset(&mc_if, 0, sizeof(mc_if)); - mc_if.s_addr = pIPAddrTable->table[i].dwAddr; + mc_if.s_addr = ipv4->sin_addr.s_addr; if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) { PRINT_SOCKET_ERROR("setsockopt"); } - ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr; + ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr; #ifndef DEBUG break; #endif } + pCurrAddresses = pCurrAddresses->Next; } } - free(pIPAddrTable); - pIPAddrTable = NULL; + if (pAddresses != NULL) { + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } } } #endif /* _WIN32 */ |