diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml index 3344323fd48..d9b95e84484 100644 --- a/.github/actions/godot-deps/action.yml +++ b/.github/actions/godot-deps/action.yml @@ -29,3 +29,7 @@ runs: python -c "import sys; print(sys.version)" python -m pip install scons==${{ inputs.scons-version }} scons --version + + - name: Setup problem matchers + shell: bash + run: echo ::add-matcher::misc/utility/problem-matchers.json diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml index af99a4b0357..ae2fc9388c3 100644 --- a/.github/workflows/godot_cpp_test.yml +++ b/.github/workflows/godot_cpp_test.yml @@ -21,7 +21,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - sparse-checkout: .github + sparse-checkout: | + .github + misc/utility/problem-matchers.json - name: Checkout godot-cpp uses: actions/checkout@v4 @@ -34,9 +36,6 @@ jobs: - name: Setup Python and SCons uses: ./.github/actions/godot-deps - - name: Setup GCC problem matcher - uses: ammaraskar/gcc-problem-matcher@master - - name: Download GDExtension interface and API dump uses: ./.github/actions/download-artifact with: diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index b594ec47c9b..6016c84af4e 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -133,9 +133,6 @@ jobs: python-version: 3.8 scons-version: 4.0 - - name: Setup GCC problem matcher - uses: ammaraskar/gcc-problem-matcher@master - - name: Compilation uses: ./.github/actions/godot-build with: @@ -158,6 +155,9 @@ jobs: - name: Build .NET solutions if: matrix.build-mono run: | + # FIXME: C# warnings should be properly handled eventually, but we don't want to clutter + # the GitHub Actions annotations, so remove the associated problem matcher for now. + echo "::remove-matcher owner=msvc::" ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd - name: Prepare artifact diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 240ea6a535f..f93474cf241 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -86,14 +86,6 @@ jobs: - name: Extract pre-built ANGLE static libraries run: Expand-Archive -Force angle/angle.zip ${{ github.workspace }}/ - - name: Setup MSVC problem matcher - if: matrix.compiler == 'msvc' - uses: ammaraskar/msvc-problem-matcher@master - - - name: Setup GCC problem matcher - if: matrix.compiler != 'msvc' - uses: ammaraskar/gcc-problem-matcher@master - - name: Compilation uses: ./.github/actions/godot-build with: diff --git a/.gitignore b/.gitignore index f72ea1ac514..22a1d53a879 100644 --- a/.gitignore +++ b/.gitignore @@ -216,6 +216,7 @@ xcuserdata/ *.xcscmblueprint *.xccheckout *.xcodeproj/* +!misc/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj ############################## ### Visual Studio specific ### diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 56f64ad0794..77418d45b03 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -184,8 +184,8 @@ repos: .*\.patch$| .*\.out$| modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines\.gd$| - modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.notest\.gd$| - modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.notest\.gd$| + modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.norun\.gd$| + modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.norun\.gd$| platform/android/java/editor/src/main/java/com/android/.*| platform/android/java/lib/src/com/google/.*| tests/data/.*\.bin$ diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 29e78f50632..607daba2fbc 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -343,6 +343,11 @@ Comment: WebP codec Copyright: 2010, Google Inc. License: BSD-3-clause +Files: ./thirdparty/manifold/ +Comment: Manifold +Copyright: 2020-2024, The Manifold Authors +License: Apache-2.0 + Files: ./thirdparty/mbedtls/ Comment: Mbed TLS Copyright: The Mbed TLS Contributors diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index f4810e4170b..070b007921d 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -751,7 +751,7 @@ Error ProjectSettings::_load_settings_binary(const String &p_path) { cs[slen] = 0; f->get_buffer((uint8_t *)cs.ptr(), slen); String key; - key.parse_utf8(cs.ptr()); + key.parse_utf8(cs.ptr(), slen); uint32_t vlen = f->get_32(); Vector d; @@ -1518,7 +1518,11 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("display/window/frame_pacing/android/enable_frame_pacing", true); GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/frame_pacing/android/swappy_mode", PROPERTY_HINT_ENUM, "pipeline_forced_on,auto_fps_pipeline_forced_on,auto_fps_auto_pipeline"), 2); - custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); +#ifdef DISABLE_DEPRECATED + custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Safe:1,Separate"); +#else + custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Unsafe (deprecated),Safe,Separate"); +#endif GLOBAL_DEF("physics/2d/run_on_separate_thread", false); GLOBAL_DEF("physics/3d/run_on_separate_thread", false); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index fadb4fed8bf..592b3eaaf02 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -579,6 +579,11 @@ String OS::get_cache_dir() const { return ::OS::get_singleton()->get_cache_path(); } +String OS::get_temp_dir() const { + // Exposed as `get_temp_dir()` instead of `get_temp_path()` for consistency with other exposed OS methods. + return ::OS::get_singleton()->get_temp_path(); +} + bool OS::is_debug_build() const { #ifdef DEBUG_ENABLED return true; @@ -707,6 +712,7 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_config_dir"), &OS::get_config_dir); ClassDB::bind_method(D_METHOD("get_data_dir"), &OS::get_data_dir); ClassDB::bind_method(D_METHOD("get_cache_dir"), &OS::get_cache_dir); + ClassDB::bind_method(D_METHOD("get_temp_dir"), &OS::get_temp_dir); ClassDB::bind_method(D_METHOD("get_unique_id"), &OS::get_unique_id); ClassDB::bind_method(D_METHOD("get_keycode_string", "code"), &OS::get_keycode_string); diff --git a/core/core_bind.h b/core/core_bind.h index 2ac76e193af..1abf3f96a7a 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -264,6 +264,7 @@ class OS : public Object { String get_config_dir() const; String get_data_dir() const; String get_cache_dir() const; + String get_temp_dir() const; Error set_thread_name(const String &p_name); ::Thread::ID get_thread_caller_id() const; diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index 0fae6c6f3dd..0f8eddc4a0d 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -157,6 +157,10 @@ class ResourceFormatLoaderCrypto : public ResourceFormatLoader { virtual void get_recognized_extensions(List *p_extensions) const override; virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; + + // Treat certificates as text files, do not generate a `*.{crt,key,pub}.uid` file. + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; } + virtual bool has_custom_uid_support() const override { return true; } }; class ResourceFormatSaverCrypto : public ResourceFormatSaver { diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 413775e0dd1..e326adda32b 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -844,7 +844,7 @@ typedef void (*GDExtensionInterfaceMemFree)(void *p_ptr); * * Logs an error to Godot's built-in debugger and to the OS terminal. * - * @param p_description The code trigging the error. + * @param p_description The code triggering the error. * @param p_function The function name where the error occurred. * @param p_file The file where the error occurred. * @param p_line The line where the error occurred. @@ -858,7 +858,7 @@ typedef void (*GDExtensionInterfacePrintError)(const char *p_description, const * * Logs an error with a message to Godot's built-in debugger and to the OS terminal. * - * @param p_description The code trigging the error. + * @param p_description The code triggering the error. * @param p_message The message to show along with the error. * @param p_function The function name where the error occurred. * @param p_file The file where the error occurred. @@ -873,7 +873,7 @@ typedef void (*GDExtensionInterfacePrintErrorWithMessage)(const char *p_descript * * Logs a warning to Godot's built-in debugger and to the OS terminal. * - * @param p_description The code trigging the warning. + * @param p_description The code triggering the warning. * @param p_function The function name where the warning occurred. * @param p_file The file where the warning occurred. * @param p_line The line where the warning occurred. @@ -887,7 +887,7 @@ typedef void (*GDExtensionInterfacePrintWarning)(const char *p_description, cons * * Logs a warning with a message to Godot's built-in debugger and to the OS terminal. * - * @param p_description The code trigging the warning. + * @param p_description The code triggering the warning. * @param p_message The message to show along with the warning. * @param p_function The function name where the warning occurred. * @param p_file The file where the warning occurred. @@ -902,7 +902,7 @@ typedef void (*GDExtensionInterfacePrintWarningWithMessage)(const char *p_descri * * Logs a script error to Godot's built-in debugger and to the OS terminal. * - * @param p_description The code trigging the error. + * @param p_description The code triggering the error. * @param p_function The function name where the error occurred. * @param p_file The file where the error occurred. * @param p_line The line where the error occurred. @@ -916,7 +916,7 @@ typedef void (*GDExtensionInterfacePrintScriptError)(const char *p_description, * * Logs a script error with a message to Godot's built-in debugger and to the OS terminal. * - * @param p_description The code trigging the error. + * @param p_description The code triggering the error. * @param p_message The message to show along with the error. * @param p_function The function name where the error occurred. * @param p_file The file where the error occurred. diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 0641ea7edf2..e773ab538d5 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -34,8 +34,8 @@ #include "core/config/project_settings.h" #include "core/io/file_access.h" -#include "core/os/memory.h" #include "core/os/os.h" +#include "core/os/time.h" #include "core/templates/local_vector.h" thread_local Error DirAccess::last_dir_open_error = OK; @@ -325,6 +325,80 @@ Ref DirAccess::create(AccessType p_access) { return da; } +Ref DirAccess::create_temp(const String &p_prefix, bool p_keep, Error *r_error) { + const String ERROR_COMMON_PREFIX = "Error while creating temporary directory"; + + if (!p_prefix.is_valid_filename()) { + *r_error = ERR_FILE_BAD_PATH; + ERR_FAIL_V_MSG(Ref(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix)); + } + + Ref dir_access = DirAccess::open(OS::get_singleton()->get_temp_path()); + + uint32_t suffix_i = 0; + String path; + while (true) { + String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", ""); + datetime += itos(Time::get_singleton()->get_ticks_usec()); + String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : ""); + path = (p_prefix.is_empty() ? "" : p_prefix + "-") + suffix; + if (!path.is_valid_filename()) { + *r_error = ERR_FILE_BAD_PATH; + return Ref(); + } + if (!DirAccess::exists(path)) { + break; + } + suffix_i += 1; + } + + Error err = dir_access->make_dir(path); + if (err != OK) { + *r_error = err; + ERR_FAIL_V_MSG(Ref(), vformat(R"(%s: "%s" couldn't create directory "%s".)", ERROR_COMMON_PREFIX, path)); + } + err = dir_access->change_dir(path); + if (err != OK) { + *r_error = err; + return Ref(); + } + + dir_access->_is_temp = true; + dir_access->_temp_keep_after_free = p_keep; + dir_access->_temp_path = dir_access->get_current_dir(); + + *r_error = OK; + return dir_access; +} + +Ref DirAccess::_create_temp(const String &p_prefix, bool p_keep) { + return create_temp(p_prefix, p_keep, &last_dir_open_error); +} + +void DirAccess::_delete_temp() { + if (!_is_temp || _temp_keep_after_free) { + return; + } + + if (!DirAccess::exists(_temp_path)) { + return; + } + + Error err; + { + Ref dir_access = DirAccess::open(_temp_path, &err); + if (err != OK) { + return; + } + err = dir_access->erase_contents_recursive(); + if (err != OK) { + return; + } + } + + DirAccess::remove_absolute(_temp_path); +} + Error DirAccess::get_open_error() { return last_dir_open_error; } @@ -557,6 +631,7 @@ bool DirAccess::is_case_sensitive(const String &p_path) const { void DirAccess::_bind_methods() { ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open); ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error); + ClassDB::bind_static_method("DirAccess", D_METHOD("create_temp", "prefix", "keep"), &DirAccess::_create_temp, DEFVAL(""), DEFVAL(false)); ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next); @@ -590,6 +665,8 @@ void DirAccess::_bind_methods() { ClassDB::bind_method(D_METHOD("read_link", "path"), &DirAccess::read_link); ClassDB::bind_method(D_METHOD("create_link", "source", "target"), &DirAccess::create_link); + ClassDB::bind_method(D_METHOD("is_bundle", "path"), &DirAccess::is_bundle); + ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational); ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational); ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden); @@ -600,3 +677,7 @@ void DirAccess::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden"); } + +DirAccess::~DirAccess() { + _delete_temp(); +} diff --git a/core/io/dir_access.h b/core/io/dir_access.h index 670c512ea96..c46ee3ada81 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -63,6 +63,13 @@ class DirAccess : public RefCounted { bool include_navigational = false; bool include_hidden = false; + bool _is_temp = false; + bool _temp_keep_after_free = false; + String _temp_path; + void _delete_temp(); + + static Ref _create_temp(const String &p_prefix = "", bool p_keep = false); + protected: static void _bind_methods(); @@ -138,6 +145,7 @@ class DirAccess : public RefCounted { } static Ref open(const String &p_path, Error *r_error = nullptr); + static Ref create_temp(const String &p_prefix = "", bool p_keep = false, Error *r_error = nullptr); static int _get_drive_count(); static String get_drive_name(int p_idx); @@ -162,9 +170,11 @@ class DirAccess : public RefCounted { bool get_include_hidden() const; virtual bool is_case_sensitive(const String &p_path) const; + virtual bool is_bundle(const String &p_file) const { return false; } +public: DirAccess() {} - virtual ~DirAccess() {} + virtual ~DirAccess(); }; #endif // DIR_ACCESS_H diff --git a/core/io/file_access.compat.inc b/core/io/file_access.compat.inc index 0ecafb7d9d4..d6a0df1a8cb 100644 --- a/core/io/file_access.compat.inc +++ b/core/io/file_access.compat.inc @@ -36,8 +36,79 @@ Ref FileAccess::_open_encrypted_bind_compat_98918(const String &p_pa return open_encrypted(p_path, p_mode_flags, p_key, Vector()); } +void FileAccess::store_8_bind_compat_78289(uint8_t p_dest) { + store_8(p_dest); +} + +void FileAccess::store_16_bind_compat_78289(uint16_t p_dest) { + store_16(p_dest); +} + +void FileAccess::store_32_bind_compat_78289(uint32_t p_dest) { + store_32(p_dest); +} + +void FileAccess::store_64_bind_compat_78289(uint64_t p_dest) { + store_64(p_dest); +} + +void FileAccess::store_buffer_bind_compat_78289(const Vector &p_buffer) { + store_buffer(p_buffer); +} + +void FileAccess::store_var_bind_compat_78289(const Variant &p_var, bool p_full_objects) { + store_var(p_var, p_full_objects); +} + +void FileAccess::store_half_bind_compat_78289(float p_dest) { + store_half(p_dest); +} + +void FileAccess::store_float_bind_compat_78289(float p_dest) { + store_float(p_dest); +} + +void FileAccess::store_double_bind_compat_78289(double p_dest) { + store_double(p_dest); +} + +void FileAccess::store_real_bind_compat_78289(real_t p_real) { + store_real(p_real); +} + +void FileAccess::store_string_bind_compat_78289(const String &p_string) { + store_string(p_string); +} + +void FileAccess::store_line_bind_compat_78289(const String &p_line) { + store_line(p_line); +} + +void FileAccess::store_csv_line_bind_compat_78289(const Vector &p_values, const String &p_delim) { + store_csv_line(p_values, p_delim); +} + +void FileAccess::store_pascal_string_bind_compat_78289(const String &p_string) { + store_pascal_string(p_string); +} + void FileAccess::_bind_compatibility_methods() { ClassDB::bind_compatibility_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::_open_encrypted_bind_compat_98918); + + ClassDB::bind_compatibility_method(D_METHOD("store_8", "value"), &FileAccess::store_8_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_16", "value"), &FileAccess::store_16_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_32", "value"), &FileAccess::store_32_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_64", "value"), &FileAccess::store_64_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_half", "value"), &FileAccess::store_half_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_float", "value"), &FileAccess::store_float_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_double", "value"), &FileAccess::store_double_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_real", "value"), &FileAccess::store_real_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_buffer", "buffer"), &FileAccess::store_buffer_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_line", "line"), &FileAccess::store_line_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line_bind_compat_78289, DEFVAL(",")); + ClassDB::bind_compatibility_method(D_METHOD("store_string", "string"), &FileAccess::store_string_bind_compat_78289); + ClassDB::bind_compatibility_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var_bind_compat_78289, DEFVAL(false)); + ClassDB::bind_compatibility_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string_bind_compat_78289); } -#endif // DISABLE_DEPRECATED +#endif diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 6aaafb16d88..9899a4ba4dd 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -40,6 +40,7 @@ #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" #include "core/os/os.h" +#include "core/os/time.h" FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = {}; @@ -86,6 +87,79 @@ Ref FileAccess::create_for_path(const String &p_path) { return ret; } +Ref FileAccess::create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep, Error *r_error) { + const String ERROR_COMMON_PREFIX = "Error while creating temporary file"; + + if (!p_prefix.is_valid_filename()) { + *r_error = ERR_FILE_BAD_PATH; + ERR_FAIL_V_MSG(Ref(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix)); + } + + if (!p_extension.is_valid_filename()) { + *r_error = ERR_FILE_BAD_PATH; + ERR_FAIL_V_MSG(Ref(), vformat(R"(%s: "%s" is not a valid extension.)", ERROR_COMMON_PREFIX, p_extension)); + } + + const String TEMP_DIR = OS::get_singleton()->get_temp_path(); + String extension = p_extension.trim_prefix("."); + + uint32_t suffix_i = 0; + String path; + while (true) { + String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", ""); + datetime += itos(Time::get_singleton()->get_ticks_usec()); + String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : ""); + path = TEMP_DIR.path_join((p_prefix.is_empty() ? "" : p_prefix + "-") + suffix + (extension.is_empty() ? "" : "." + extension)); + if (!DirAccess::exists(path)) { + break; + } + suffix_i += 1; + } + + Error err; + { + // Create file first with WRITE mode. + // Otherwise, it would fail to open with a READ mode. + Ref ret = FileAccess::open(path, FileAccess::ModeFlags::WRITE, &err); + if (err != OK) { + *r_error = err; + ERR_FAIL_V_MSG(Ref(), vformat(R"(%s: could not create "%s".)", ERROR_COMMON_PREFIX, path)); + } + ret->flush(); + } + + // Open then the temp file with the correct mode flag. + Ref ret = FileAccess::open(path, p_mode_flags, &err); + if (err != OK) { + *r_error = err; + ERR_FAIL_V_MSG(Ref(), vformat(R"(%s: could not open "%s".)", ERROR_COMMON_PREFIX, path)); + } + if (ret.is_valid()) { + ret->_is_temp_file = true; + ret->_temp_keep_after_use = p_keep; + ret->_temp_path = ret->get_path_absolute(); + } + + *r_error = OK; + return ret; +} + +Ref FileAccess::_create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep) { + return create_temp(p_mode_flags, p_prefix, p_extension, p_keep, &last_file_open_error); +} + +void FileAccess::_delete_temp() { + if (!_is_temp_file || _temp_keep_after_use) { + return; + } + + if (!FileAccess::exists(_temp_path)) { + return; + } + + DirAccess::remove_absolute(_temp_path); +} + Error FileAccess::reopen(const String &p_path, int p_mode_flags) { return open_internal(p_path, p_mode_flags); } @@ -493,60 +567,60 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const { w[len] = 0; String s; - s.parse_utf8((const char *)w, -1, p_skip_cr); + s.parse_utf8((const char *)w, len, p_skip_cr); return s; } -void FileAccess::store_8(uint8_t p_dest) { - store_buffer(&p_dest, sizeof(uint8_t)); +bool FileAccess::store_8(uint8_t p_dest) { + return store_buffer(&p_dest, sizeof(uint8_t)); } -void FileAccess::store_16(uint16_t p_dest) { +bool FileAccess::store_16(uint16_t p_dest) { if (big_endian) { p_dest = BSWAP16(p_dest); } - store_buffer(reinterpret_cast(&p_dest), sizeof(uint16_t)); + return store_buffer(reinterpret_cast(&p_dest), sizeof(uint16_t)); } -void FileAccess::store_32(uint32_t p_dest) { +bool FileAccess::store_32(uint32_t p_dest) { if (big_endian) { p_dest = BSWAP32(p_dest); } - store_buffer(reinterpret_cast(&p_dest), sizeof(uint32_t)); + return store_buffer(reinterpret_cast(&p_dest), sizeof(uint32_t)); } -void FileAccess::store_64(uint64_t p_dest) { +bool FileAccess::store_64(uint64_t p_dest) { if (big_endian) { p_dest = BSWAP64(p_dest); } - store_buffer(reinterpret_cast(&p_dest), sizeof(uint64_t)); + return store_buffer(reinterpret_cast(&p_dest), sizeof(uint64_t)); } -void FileAccess::store_real(real_t p_real) { +bool FileAccess::store_real(real_t p_real) { if constexpr (sizeof(real_t) == 4) { - store_float(p_real); + return store_float(p_real); } else { - store_double(p_real); + return store_double(p_real); } } -void FileAccess::store_half(float p_dest) { - store_16(Math::make_half_float(p_dest)); +bool FileAccess::store_half(float p_dest) { + return store_16(Math::make_half_float(p_dest)); } -void FileAccess::store_float(float p_dest) { +bool FileAccess::store_float(float p_dest) { MarshallFloat m; m.f = p_dest; - store_32(m.i); + return store_32(m.i); } -void FileAccess::store_double(double p_dest) { +bool FileAccess::store_double(double p_dest) { MarshallDouble m; m.d = p_dest; - store_64(m.l); + return store_64(m.l); } uint64_t FileAccess::get_modified_time(const String &p_file) { @@ -630,19 +704,18 @@ Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) { return err; } -void FileAccess::store_string(const String &p_string) { +bool FileAccess::store_string(const String &p_string) { if (p_string.length() == 0) { - return; + return true; } CharString cs = p_string.utf8(); - store_buffer((uint8_t *)&cs[0], cs.length()); + return store_buffer((uint8_t *)&cs[0], cs.length()); } -void FileAccess::store_pascal_string(const String &p_string) { +bool FileAccess::store_pascal_string(const String &p_string) { CharString cs = p_string.utf8(); - store_32(cs.length()); - store_buffer((uint8_t *)&cs[0], cs.length()); + return store_32(cs.length()) && store_buffer((uint8_t *)&cs[0], cs.length()); } String FileAccess::get_pascal_string() { @@ -653,17 +726,16 @@ String FileAccess::get_pascal_string() { cs[sl] = 0; String ret; - ret.parse_utf8(cs.ptr()); + ret.parse_utf8(cs.ptr(), sl); return ret; } -void FileAccess::store_line(const String &p_line) { - store_string(p_line); - store_8('\n'); +bool FileAccess::store_line(const String &p_line) { + return store_string(p_line) && store_8('\n'); } -void FileAccess::store_csv_line(const Vector &p_values, const String &p_delim) { - ERR_FAIL_COND(p_delim.length() != 1); +bool FileAccess::store_csv_line(const Vector &p_values, const String &p_delim) { + ERR_FAIL_COND_V(p_delim.length() != 1, false); String line = ""; int size = p_values.size(); @@ -680,30 +752,43 @@ void FileAccess::store_csv_line(const Vector &p_values, const String &p_ line += value; } - store_line(line); + return store_line(line); } -void FileAccess::store_buffer(const Vector &p_buffer) { +bool FileAccess::store_buffer(const Vector &p_buffer) { uint64_t len = p_buffer.size(); + if (len == 0) { + return true; + } + const uint8_t *r = p_buffer.ptr(); - store_buffer(r, len); + return store_buffer(r, len); } -void FileAccess::store_var(const Variant &p_var, bool p_full_objects) { +bool FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND_V(!p_src && p_length > 0, false); + for (uint64_t i = 0; i < p_length; i++) { + if (unlikely(!store_8(p_src[i]))) { + return false; + } + } + return true; +} + +bool FileAccess::store_var(const Variant &p_var, bool p_full_objects) { int len; Error err = encode_variant(p_var, nullptr, len, p_full_objects); - ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); + ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant."); Vector buff; buff.resize(len); uint8_t *w = buff.ptrw(); err = encode_variant(p_var, &w[0], len, p_full_objects); - ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); + ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant."); - store_32(len); - store_buffer(buff); + return store_32(len) && store_buffer(buff); } Vector FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) { @@ -825,6 +910,7 @@ void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass); ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0)); ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error); + ClassDB::bind_static_method("FileAccess", D_METHOD("create_temp", "mode_flags", "prefix", "extension", "keep"), &FileAccess::_create_temp, DEFVAL(""), DEFVAL(""), DEFVAL(false)); ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_bytes", "path"), &FileAccess::_get_file_as_bytes); ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_string", "path"), &FileAccess::_get_file_as_string); @@ -866,7 +952,7 @@ void FileAccess::_bind_methods() { ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float); ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double); ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real); - ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (void(FileAccess::*)(const Vector &)) & FileAccess::store_buffer); + ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (bool(FileAccess::*)(const Vector &)) & FileAccess::store_buffer); ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line); ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string); @@ -914,3 +1000,7 @@ void FileAccess::_bind_methods() { BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID); BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE); } + +FileAccess::~FileAccess() { + _delete_temp(); +} diff --git a/core/io/file_access.h b/core/io/file_access.h index 3cca8ba4f04..36486b6af6b 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -114,6 +114,21 @@ class FileAccess : public RefCounted { #ifndef DISABLE_DEPRECATED static Ref _open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector &p_key); + void store_8_bind_compat_78289(uint8_t p_dest); + void store_16_bind_compat_78289(uint16_t p_dest); + void store_32_bind_compat_78289(uint32_t p_dest); + void store_64_bind_compat_78289(uint64_t p_dest); + void store_buffer_bind_compat_78289(const Vector &p_buffer); + void store_var_bind_compat_78289(const Variant &p_var, bool p_full_objects = false); + void store_half_bind_compat_78289(float p_dest); + void store_float_bind_compat_78289(float p_dest); + void store_double_bind_compat_78289(double p_dest); + void store_real_bind_compat_78289(real_t p_real); + void store_string_bind_compat_78289(const String &p_string); + void store_line_bind_compat_78289(const String &p_line); + void store_csv_line_bind_compat_78289(const Vector &p_values, const String &p_delim = ","); + void store_pascal_string_bind_compat_78289(const String &p_string); + static void _bind_compatibility_methods(); #endif @@ -130,6 +145,13 @@ class FileAccess : public RefCounted { static Ref _open(const String &p_path, ModeFlags p_mode_flags); + bool _is_temp_file = false; + bool _temp_keep_after_use = false; + String _temp_path; + void _delete_temp(); + + static Ref _create_temp(int p_mode_flags, const String &p_prefix = "", const String &p_extension = "", bool p_keep = false); + public: static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; } @@ -166,6 +188,7 @@ class FileAccess : public RefCounted { virtual String get_as_utf8_string(bool p_skip_cr = false) const; /** + * Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) * It's not about the current CPU type but file formats. * This flag gets reset to `false` (little endian) on each open. @@ -177,27 +200,27 @@ class FileAccess : public RefCounted { virtual Error resize(int64_t p_length) = 0; virtual void flush() = 0; - virtual void store_8(uint8_t p_dest); ///< store a byte - virtual void store_16(uint16_t p_dest); ///< store 16 bits uint - virtual void store_32(uint32_t p_dest); ///< store 32 bits uint - virtual void store_64(uint64_t p_dest); ///< store 64 bits uint + virtual bool store_8(uint8_t p_dest); ///< store a byte + virtual bool store_16(uint16_t p_dest); ///< store 16 bits uint + virtual bool store_32(uint32_t p_dest); ///< store 32 bits uint + virtual bool store_64(uint64_t p_dest); ///< store 64 bits uint - virtual void store_half(float p_dest); - virtual void store_float(float p_dest); - virtual void store_double(double p_dest); - virtual void store_real(real_t p_real); + virtual bool store_half(float p_dest); + virtual bool store_float(float p_dest); + virtual bool store_double(double p_dest); + virtual bool store_real(real_t p_real); - virtual void store_string(const String &p_string); - virtual void store_line(const String &p_line); - virtual void store_csv_line(const Vector &p_values, const String &p_delim = ","); + virtual bool store_string(const String &p_string); + virtual bool store_line(const String &p_line); + virtual bool store_csv_line(const Vector &p_values, const String &p_delim = ","); - virtual void store_pascal_string(const String &p_string); + virtual bool store_pascal_string(const String &p_string); virtual String get_pascal_string(); - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children. - void store_buffer(const Vector &p_buffer); + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children. + bool store_buffer(const Vector &p_buffer); - void store_var(const Variant &p_var, bool p_full_objects = false); + bool store_var(const Variant &p_var, bool p_full_objects = false); virtual void close() = 0; @@ -208,6 +231,7 @@ class FileAccess : public RefCounted { static Ref create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files. static Ref create_for_path(const String &p_path); static Ref open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files. + static Ref create_temp(int p_mode_flags, const String &p_prefix = "", const String &p_extension = "", bool p_keep = false, Error *r_error = nullptr); static Ref open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector &p_key, const Vector &p_iv = Vector()); static Ref open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); @@ -243,8 +267,9 @@ class FileAccess : public RefCounted { create_func[p_access] = _create_builtin; } +public: FileAccess() {} - virtual ~FileAccess() {} + virtual ~FileAccess(); }; VARIANT_ENUM_CAST(FileAccess::CompressionMode); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 84137251efa..c3182663581 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -42,18 +42,6 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_ block_size = p_block_size; } -#define WRITE_FIT(m_bytes) \ - { \ - if (write_pos + (m_bytes) > write_max) { \ - write_max = write_pos + (m_bytes); \ - } \ - if (write_max > write_buffer_size) { \ - write_buffer_size = next_power_of_2(write_max); \ - buffer.resize(write_buffer_size); \ - write_ptr = buffer.ptrw(); \ - } \ - } - Error FileAccessCompressed::open_after_magic(Ref p_base) { f = p_base; cmode = (Compression::Mode)f->get_32(); @@ -311,13 +299,23 @@ void FileAccessCompressed::flush() { // compressed files keep data in memory till close() } -void FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); - ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); +bool FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND_V_MSG(f.is_null(), false, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode."); + + if (write_pos + (p_length) > write_max) { + write_max = write_pos + (p_length); + } + if (write_max > write_buffer_size) { + write_buffer_size = next_power_of_2(write_max); + ERR_FAIL_COND_V(buffer.resize(write_buffer_size) != OK, false); + write_ptr = buffer.ptrw(); + } - WRITE_FIT(p_length); memcpy(write_ptr + write_pos, p_src, p_length); + write_pos += p_length; + return true; } bool FileAccessCompressed::file_exists(const String &p_name) { diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 763f76f051f..22bce3d318b 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -91,7 +91,7 @@ class FileAccessCompressed : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 5fe7d99c1c7..dcc16da6bcf 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -230,16 +230,17 @@ Error FileAccessEncrypted::get_error() const { return eofed ? ERR_FILE_EOF : OK; } -void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); - ERR_FAIL_COND(!p_src && p_length > 0); +bool FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode."); + ERR_FAIL_COND_V(!p_src && p_length > 0, false); if (pos + p_length >= get_length()) { - data.resize(pos + p_length); + ERR_FAIL_COND_V(data.resize(pos + p_length) != OK, false); } memcpy(data.ptrw() + pos, p_src, p_length); pos += p_length; + return true; } void FileAccessEncrypted::flush() { diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 0a8c19ea37f..e184d54870f 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -84,7 +84,7 @@ class FileAccessEncrypted : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 2ead49ba81e..efe1de96a91 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -149,16 +149,16 @@ void FileAccessMemory::flush() { ERR_FAIL_NULL(data); } -void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_COND(!p_src && p_length > 0); +bool FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND_V(!p_src && p_length > 0, false); uint64_t left = length - pos; uint64_t write = MIN(p_length, left); - if (write < p_length) { - WARN_PRINT("Writing less data than requested"); - } - memcpy(&data[pos], p_src, write); pos += write; + + ERR_FAIL_COND_V_MSG(write < p_length, false, "Writing less data than requested."); + + return true; } diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 6e8691ab290..5892caf318f 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -63,7 +63,7 @@ class FileAccessMemory : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 0e8cb8a907f..202e6ee8390 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -311,7 +311,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, cs[sl] = 0; String path; - path.parse_utf8(cs.ptr()); + path.parse_utf8(cs.ptr(), sl); uint64_t ofs = f->get_64(); uint64_t size = f->get_64(); @@ -419,8 +419,8 @@ void FileAccessPack::flush() { ERR_FAIL(); } -void FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL(); +bool FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_V(false); } bool FileAccessPack::file_exists(const String &p_name) { diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index ff02d3f6fa6..0e620fc24b8 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -186,7 +186,7 @@ class FileAccessPack : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_name) override; diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 71ce18145a2..3c8b7b9de94 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -324,8 +324,8 @@ void FileAccessZip::flush() { ERR_FAIL(); } -void FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL(); +bool FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_V(false); } bool FileAccessZip::file_exists(const String &p_name) { diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index c2daf10f47c..b4f84c24d69 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -103,7 +103,7 @@ class FileAccessZip : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index b1dedd1396c..19d225cc905 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -486,7 +486,7 @@ Error HTTPClientTCP::poll() { // End of response, parse. response_str.push_back(0); String response; - response.parse_utf8((const char *)response_str.ptr()); + response.parse_utf8((const char *)response_str.ptr(), response_str.size()); Vector responses = response.split("\n"); body_size = -1; chunked = false; diff --git a/core/io/image.cpp b/core/io/image.cpp index cd86ca7bca8..8a5e6abbbda 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2790,8 +2790,7 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels } break; case COMPRESS_S3TC: { - // BC3 is unsupported currently. - if ((p_channels == USED_CHANNELS_R || p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) { + if (_image_compress_bc_rd_func) { Error result = _image_compress_bc_rd_func(this, p_channels); // If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme. diff --git a/core/io/json.h b/core/io/json.h index a16e15bc00d..7fc8ad10b8e 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -107,6 +107,10 @@ class ResourceFormatLoaderJSON : public ResourceFormatLoader { virtual void get_recognized_extensions(List *p_extensions) const override; virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; + + // Treat JSON as a text file, do not generate a `*.json.uid` file. + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; } + virtual bool has_custom_uid_support() const override { return true; } }; class ResourceFormatSaverJSON : public ResourceFormatSaver { diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index e27873d024d..edcb6be0511 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -167,7 +167,7 @@ StringName ResourceLoaderBinary::_get_string() { } f->get_buffer((uint8_t *)&str_buf[0], len); String s; - s.parse_utf8(&str_buf[0]); + s.parse_utf8(&str_buf[0], len); return s; } @@ -923,7 +923,7 @@ static String get_ustring(Ref f) { str_buf.resize(len); f->get_buffer((uint8_t *)&str_buf[0], len); String s; - s.parse_utf8(&str_buf[0]); + s.parse_utf8(&str_buf[0], len); return s; } @@ -937,7 +937,7 @@ String ResourceLoaderBinary::get_unicode_string() { } f->get_buffer((uint8_t *)&str_buf[0], len); String s; - s.parse_utf8(&str_buf[0]); + s.parse_utf8(&str_buf[0], len); return s; } diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index bd276010bf9..fb9992d6d5d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -887,7 +887,7 @@ bool ResourceLoader::_ensure_load_progress() { // Some servers may need a new engine iteration to allow the load to progress. // Since the only known one is the rendering server (in single thread mode), let's keep it simple and just sync it. // This may be refactored in the future to support other servers and have less coupling. - if (OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD) { + if (OS::get_singleton()->is_separate_thread_rendering_enabled()) { return false; // Not needed. } RenderingServer::get_singleton()->sync(); diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 83cde973319..a73695a92a6 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -147,7 +147,12 @@ String ResourceUID::uid_to_path(const String &p_uid) { } String ResourceUID::path_to_uid(const String &p_path) { - return singleton->id_to_text(ResourceLoader::get_resource_uid(p_path)); + const ID id = ResourceLoader::get_resource_uid(p_path); + if (id == INVALID_ID) { + return p_path; + } else { + return singleton->id_to_text(id); + } } String ResourceUID::ensure_path(const String &p_uid_or_path) { diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 9b17c0ecc50..335a71e1bd9 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -45,6 +45,10 @@ class TranslationLoaderPO : public ResourceFormatLoader { virtual bool handles_type(const String &p_type) const override; virtual String get_resource_type(const String &p_path) const override; + // Treat translations as text/binary files, do not generate a `*.{po,mo}.uid` file. + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; } + virtual bool has_custom_uid_support() const override { return true; } + TranslationLoaderPO() {} }; diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 67934a73133..1b66a51b119 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -42,7 +42,7 @@ int64_t AStar3D::get_available_point_id() const { while (points.has(cur_new_id)) { cur_new_id++; } - const_cast(last_free_id) = cur_new_id; + last_free_id = cur_new_id; } return last_free_id; diff --git a/core/math/a_star.h b/core/math/a_star.h index d322ffeebe3..f2274d20edb 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -110,7 +110,7 @@ class AStar3D : public RefCounted { } }; - int64_t last_free_id = 0; + mutable int64_t last_free_id = 0; uint64_t pass = 1; OAHashMap points; diff --git a/core/object/object.h b/core/object/object.h index 666537394ad..3208b9d4ad3 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -574,7 +574,7 @@ class Object { CONNECT_PERSIST = 2, // hint for scene to save this connection CONNECT_ONE_SHOT = 4, CONNECT_REFERENCE_COUNTED = 8, - CONNECT_INHERITED = 16, // Used in editor builds. + CONNECT_INHERITED = 16, // Whether or not the connection is in an instance of a scene. }; struct Connection { diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 23c0bf48293..a8399db53dc 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -224,7 +224,7 @@ struct PtrToArg> { return Ref(); } // p_ptr points to a RefCounted object - return Ref(const_cast(*reinterpret_cast(p_ptr))); + return Ref(*reinterpret_cast(p_ptr)); } typedef Ref EncodeT; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 15fa7e13f86..325163ceec5 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -570,7 +570,7 @@ void ScriptLanguage::frame() { } TypedArray ScriptLanguage::CodeCompletionOption::get_option_characteristics(const String &p_base) { - // Return characacteristics of the match found by order of importance. + // Return characteristics of the match found by order of importance. // Matches will be ranked by a lexicographical order on the vector returned by this function. // The lower values indicate better matches and that they should go before in the order of appearance. if (last_matches == matches) { diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h index 39106cffe1f..8457daaa8f4 100644 --- a/core/os/condition_variable.h +++ b/core/os/condition_variable.h @@ -59,12 +59,12 @@ class ConditionVariable { public: template _ALWAYS_INLINE_ void wait(const MutexLock &p_lock) const { - condition.wait(const_cast &>(p_lock._get_lock())); + condition.wait(p_lock._get_lock()); } template _ALWAYS_INLINE_ void wait(const MutexLock> &p_lock) const { - condition.wait(const_cast &>(p_lock.mutex._get_lock())); + condition.wait(p_lock.mutex._get_lock()); } _ALWAYS_INLINE_ void notify_one() const { diff --git a/core/os/os.cpp b/core/os/os.cpp index fda9cc55123..1887bf92722 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -278,6 +278,10 @@ String OS::get_cache_path() const { return "."; } +String OS::get_temp_path() const { + return "."; +} + // Path to macOS .app bundle resources String OS::get_bundle_resource_dir() const { return "."; @@ -538,14 +542,9 @@ bool OS::has_feature(const String &p_feature) { return true; } - if (has_server_feature_callback) { - return has_server_feature_callback(p_feature); - } -#ifdef DEBUG_ENABLED - else if (is_stdout_verbose()) { - WARN_PRINT_ONCE("Server features cannot be checked before RenderingServer has been created. If you are checking a server feature, consider moving your OS::has_feature call after INITIALIZATION_LEVEL_SERVERS."); + if (has_server_feature_callback && has_server_feature_callback(p_feature)) { + return true; } -#endif if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) { return true; diff --git a/core/os/os.h b/core/os/os.h index b5d247b3134..5935421cccc 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -113,7 +113,7 @@ class OS { friend int test_main(int argc, char *argv[]); HasServerFeatureCallback has_server_feature_callback = nullptr; - RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE; + bool _separate_thread_render = false; // Functions used by Main to initialize/deinitialize the OS. void add_logger(Logger *p_logger); @@ -276,7 +276,7 @@ class OS { virtual uint64_t get_static_memory_peak_usage() const; virtual Dictionary get_memory_info() const; - RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; } + bool is_separate_thread_rendering_enabled() const { return _separate_thread_render; } virtual String get_locale() const; String get_locale_language() const; @@ -289,6 +289,7 @@ class OS { virtual String get_data_path() const; virtual String get_config_path() const; virtual String get_cache_path() const; + virtual String get_temp_path() const; virtual String get_bundle_resource_dir() const; virtual String get_bundle_icon_path() const; diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index a9b414bb38d..d3aa80ba3b3 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -193,12 +193,6 @@ bool StringName::operator!=(const char *p_name) const { return !(operator==(p_name)); } -bool StringName::operator!=(const StringName &p_name) const { - // the real magic of all this mess happens here. - // this is why path comparisons are very fast - return _data != p_name._data; -} - char32_t StringName::operator[](int p_index) const { if (_data) { if (_data->cname) { diff --git a/core/string/string_name.h b/core/string/string_name.h index 28cc5741c10..3412dc544ff 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -134,10 +134,13 @@ class StringName { return _data >= p_name._data; } _FORCE_INLINE_ bool operator==(const StringName &p_name) const { - // the real magic of all this mess happens here. - // this is why path comparisons are very fast + // The real magic of all this mess happens here. + // This is why path comparisons are very fast. return _data == p_name._data; } + _FORCE_INLINE_ bool operator!=(const StringName &p_name) const { + return _data != p_name._data; + } _FORCE_INLINE_ uint32_t hash() const { if (_data) { return _data->hash; @@ -148,7 +151,6 @@ class StringName { _FORCE_INLINE_ const void *data_unique_pointer() const { return (void *)_data; } - bool operator!=(const StringName &p_name) const; _FORCE_INLINE_ operator String() const { if (_data) { diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp index e598e80ea09..8bd20079c8c 100644 --- a/core/string/translation_server.cpp +++ b/core/string/translation_server.cpp @@ -485,6 +485,7 @@ void TranslationServer::setup() { main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true)); #ifdef TOOLS_ENABLED + ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/test", PROPERTY_HINT_LOCALE_ID, "")); ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, "")); #endif } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 6cbef7108bc..ccb5566ecef 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1280,7 +1280,7 @@ String String::get_slice(const char *p_splitter, int p_slice) const { } int i = 0; - int splitter_length = strlen(p_splitter); + const int splitter_length = strlen(p_splitter); while (true) { pos = find(p_splitter, pos); if (pos == -1) { @@ -1431,6 +1431,7 @@ Vector String::split(const char *p_splitter, bool p_allow_empty, int p_m int from = 0; int len = length(); + const int splitter_length = strlen(p_splitter); while (true) { int end; @@ -1461,7 +1462,7 @@ Vector String::split(const char *p_splitter, bool p_allow_empty, int p_m break; } - from = end + strlen(p_splitter); + from = end + splitter_length; } return ret; diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp index d7517662e2f..748b891a96a 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -695,6 +695,15 @@ Dictionary::Dictionary() { _p->refcount.init(); } +Dictionary::Dictionary(std::initializer_list> p_init) { + _p = memnew(DictionaryPrivate); + _p->refcount.init(); + + for (const KeyValue &E : p_init) { + operator[](E.key) = E.value; + } +} + Dictionary::~Dictionary() { _unref(); } diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index e4d7bf95c3a..8b62e9e2286 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -35,6 +35,7 @@ #include "core/string/ustring.h" #include "core/templates/list.h" +#include "core/templates/pair.h" #include "core/variant/array.h" class Variant; @@ -114,6 +115,7 @@ class Dictionary { Dictionary(const Dictionary &p_base, uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script); Dictionary(const Dictionary &p_from); + Dictionary(std::initializer_list> p_init); Dictionary(); ~Dictionary(); }; diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h index cfb6cda61cd..540fb1141fb 100644 --- a/core/variant/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -162,7 +162,7 @@ MAKE_PTRARG_BY_REFERENCE(Variant); template struct PtrToArg { _FORCE_INLINE_ static T *convert(const void *p_ptr) { - return likely(p_ptr) ? const_cast(*reinterpret_cast(p_ptr)) : nullptr; + return likely(p_ptr) ? *reinterpret_cast(p_ptr) : nullptr; } typedef Object *EncodeT; _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { diff --git a/core/variant/typed_dictionary.h b/core/variant/typed_dictionary.h index 1b96f18d869..b8ac89eb04d 100644 --- a/core/variant/typed_dictionary.h +++ b/core/variant/typed_dictionary.h @@ -61,6 +61,14 @@ class TypedDictionary : public Dictionary { _FORCE_INLINE_ TypedDictionary() { set_typed(Variant::OBJECT, K::get_class_static(), Variant(), Variant::OBJECT, V::get_class_static(), Variant()); } + + _FORCE_INLINE_ TypedDictionary(std::initializer_list> p_init) : + Dictionary() { + set_typed(Variant::OBJECT, K::get_class_static(), Variant(), Variant::OBJECT, V::get_class_static(), Variant()); + for (const KeyValue &E : p_init) { + operator[](E.key) = E.value; + } + } }; template @@ -137,6 +145,13 @@ struct GetTypeInfo &> { _FORCE_INLINE_ TypedDictionary() { \ set_typed(Variant::OBJECT, T::get_class_static(), Variant(), m_variant_type, StringName(), Variant()); \ } \ + _FORCE_INLINE_ TypedDictionary(std::initializer_list> p_init) : \ + Dictionary() { \ + set_typed(Variant::OBJECT, T::get_class_static(), Variant(), m_variant_type, StringName(), Variant()); \ + for (const KeyValue &E : p_init) { \ + operator[](E.key) = E.value; \ + } \ + } \ }; \ template \ struct GetTypeInfo> { \ @@ -219,6 +234,13 @@ struct GetTypeInfo &> { _FORCE_INLINE_ TypedDictionary() { \ set_typed(m_variant_type_key, StringName(), Variant(), m_variant_type_value, StringName(), Variant()); \ } \ + _FORCE_INLINE_ TypedDictionary(std::initializer_list> p_init) : \ + Dictionary() { \ + set_typed(m_variant_type_key, StringName(), Variant(), m_variant_type_value, StringName(), Variant()); \ + for (const KeyValue &E : p_init) { \ + operator[](E.key) = E.value; \ + } \ + } \ }; \ template <> \ struct GetTypeInfo> { \ diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index c6a1663da56..6fbbca1cb6c 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -370,7 +370,7 @@ Returns an "eased" value of [param x] based on an easing function defined with [param curve]. This easing function is based on an exponent. The [param curve] can be any floating-point number, with specific values leading to the following behaviors: [codeblock lang=text] - Lower than -1.0 (exclusive): Ease in-out - - 1.0: Linear + - -1.0: Linear - Between -1.0 and 0.0 (exclusive): Ease out-in - 0.0: Constant - Between 0.0 to 1.0 (exclusive): Ease out diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml index b2a8002d742..28bea47e219 100644 --- a/doc/classes/AnimationNodeOneShot.xml +++ b/doc/classes/AnimationNodeOneShot.xml @@ -70,14 +70,14 @@ If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping. - Determines how cross-fading between animations is eased. If empty, the transition will be linear. + Determines how cross-fading between animations is eased. If empty, the transition will be linear. Should be a unit [Curve]. The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 0 second and ends at 1 second during the animation. [b]Note:[/b] [AnimationNodeOneShot] transitions the current state after the end of the fading. When [AnimationNodeOutput] is considered as the most upstream, so the [member fadein_time] is scaled depending on the downstream delta. For example, if this value is set to [code]1.0[/code] and a [AnimationNodeTimeScale] with a value of [code]2.0[/code] is chained downstream, the actual processing time will be 0.5 second. - Determines how cross-fading between animations is eased. If empty, the transition will be linear. + Determines how cross-fading between animations is eased. If empty, the transition will be linear. Should be a unit [Curve]. The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 4 second and ends at 5 second during the animation. diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml index c729eeebbae..d7dffa69162 100644 --- a/doc/classes/AnimationNodeStateMachineTransition.xml +++ b/doc/classes/AnimationNodeStateMachineTransition.xml @@ -41,7 +41,7 @@ The transition type. - Ease curve for better control over cross-fade between this state and the next. + Ease curve for better control over cross-fade between this state and the next. Should be a unit [Curve]. The time to cross-fade between this state and the next. diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml index 382166d823e..af80fef73a2 100644 --- a/doc/classes/AnimationNodeTransition.xml +++ b/doc/classes/AnimationNodeTransition.xml @@ -96,7 +96,7 @@ The number of enabled input ports for this animation node. - Determines how cross-fading between animations is eased. If empty, the transition will be linear. + Determines how cross-fading between animations is eased. If empty, the transition will be linear. Should be a unit [Curve]. Cross-fading time (in seconds) between each animation connected to the inputs. diff --git a/doc/classes/AudioStreamWAV.xml b/doc/classes/AudioStreamWAV.xml index 8d882deaee2..566109c0437 100644 --- a/doc/classes/AudioStreamWAV.xml +++ b/doc/classes/AudioStreamWAV.xml @@ -11,6 +11,38 @@ $DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html + + + + + + Creates a new [AudioStreamWAV] instance from the given buffer. The keys and values of [param options] match the properties of [ResourceImporterWAV]. + The usage of [param options] is identical to [method AudioStreamWAV.load_from_file]. + + + + + + + + Creates a new [AudioStreamWAV] instance from the given file path. The keys and values of [param options] match the properties of [ResourceImporterWAV]. + [b]Example:[/b] Load the first file dropped as a WAV and play it: + [codeblock] + @onready var audio_player = $AudioStreamPlayer + + func _ready(): + get_window().files_dropped.connect(_on_files_dropped) + + func _on_files_dropped(files): + if files[0].get_extension() == "wav": + audio_player.stream = AudioStreamWAV.load_from_file(files[0], { + "force/max_rate": true, + "force/max_rate_hz": 11025 + }) + audio_player.play() + [/codeblock] + + diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 81fd60e0156..669f1e455f1 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -88,7 +88,7 @@ The strength of the anisotropy effect. This is multiplied by [member anisotropy_flowmap]'s alpha channel if a texture is defined there and the texture contains an alpha channel. - If [code]true[/code], anisotropy is enabled. Anisotropy changes the shape of the specular blob and aligns it to tangent space. This is useful for brushed aluminium and hair reflections. + If [code]true[/code], anisotropy is enabled. Anisotropy changes the shape of the specular blob and aligns it to tangent space. This is useful for brushed aluminum and hair reflections. [b]Note:[/b] Mesh tangents are needed for anisotropy to work. If the mesh does not contain tangents, the anisotropy effect will appear broken. [b]Note:[/b] Material anisotropy should not to be confused with anisotropic texture filtering, which can be enabled by setting [member texture_filter] to [constant TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC]. @@ -125,7 +125,6 @@ Controls how the object faces the camera. See [enum BillboardMode]. - [b]Note:[/b] When billboarding is enabled and the material also casts shadows, billboards will face [b]the[/b] camera in the scene when rendering shadows. In scenes with multiple cameras, the intended shadow cannot be determined and this will result in undefined behavior. See [url=https://github.com/godotengine/godot/pull/72638]GitHub Pull Request #72638[/url] for details. [b]Note:[/b] Billboard mode is not suitable for VR because the left-right vector of the camera is not horizontal when the screen is attached to your head instead of on the table. See [url=https://github.com/godotengine/godot/issues/41567]GitHub issue #41567[/url] for details. diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index 95db54da647..704cee6c4a1 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -6,7 +6,12 @@ The [Basis] built-in [Variant] type is a 3×3 [url=https://en.wikipedia.org/wiki/Matrix_(mathematics)]matrix[/url] used to represent 3D rotation, scale, and shear. It is frequently used within a [Transform3D]. A [Basis] is composed by 3 axis vectors, each representing a column of the matrix: [member x], [member y], and [member z]. The length of each axis ([method Vector3.length]) influences the basis's scale, while the direction of all axes influence the rotation. Usually, these axes are perpendicular to one another. However, when you rotate any axis individually, the basis becomes sheared. Applying a sheared basis to a 3D model will make the model appear distorted. - A [Basis] is [b]orthogonal[/b] if its axes are perpendicular to each other. A basis is [b]normalized[/b] if the length of every axis is [code]1[/code]. A basis is [b]uniform[/b] if all axes share the same length (see [method get_scale]). A basis is [b]orthonormal[/b] if it is both orthogonal and normalized, which allows it to only represent rotations. A basis is [b]conformal[/b] if it is both orthogonal and uniform, which ensures it is not distorted. + A [Basis] is: + - [b]Orthogonal[/b] if its axes are perpendicular to each other. + - [b]Normalized[/b] if the length of every axis is [code]1.0[/code]. + - [b]Uniform[/b] if all axes share the same length (see [method get_scale]). + - [b]Orthonormal[/b] if it is both orthogonal and normalized, which allows it to only represent rotations (see [method orthonormalized]). + - [b]Conformal[/b] if it is both orthogonal and uniform, which ensures it is not distorted. For a general introduction, see the [url=$DOCS_URL/tutorials/math/matrices_and_transforms.html]Matrices and transforms[/url] tutorial. [b]Note:[/b] Redot uses a [url=https://en.wikipedia.org/wiki/Right-hand_rule]right-handed coordinate system[/url], which is a common standard. For directions, the convention for built-in types like [Camera3D] is for -Z to point forward (+X is right, +Y is up, and +Z is back). Other objects may use different direction conventions. For more information, see the [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/model_export_considerations.html#d-asset-direction-conventions]3D asset direction conventions[/url] tutorial. [b]Note:[/b] The basis matrices are exposed as [url=https://www.mindcontrol.org/~hplus/graphics/matrix-layout.html]column-major[/url] order, which is the same as OpenGL. However, they are stored internally in row-major order, which is the same as DirectX. @@ -24,7 +29,7 @@ - Constructs a [Basis] identical to the [constant IDENTITY]. + Constructs a [Basis] identical to [constant IDENTITY]. [b]Note:[/b] In C#, this constructs a [Basis] with all of its components set to [constant Vector3.ZERO]. @@ -67,7 +72,7 @@ Returns the [url=https://en.wikipedia.org/wiki/Determinant]determinant[/url] of this basis's matrix. For advanced math, this number can be used to determine a few attributes: - - If the determinant is exactly [code]0[/code], the basis is not invertible (see [method inverse]). + - If the determinant is exactly [code]0.0[/code], the basis is not invertible (see [method inverse]). - If the determinant is a negative number, the basis represents a negative scale. [b]Note:[/b] If the basis's scale is the same for every axis, its determinant is always that scale by the power of 2. @@ -78,21 +83,21 @@ Constructs a new [Basis] that only represents rotation from the given [Vector3] of [url=https://en.wikipedia.org/wiki/Euler_angles]Euler angles[/url], in radians. - - The [member Vector3.x] should contain the angle around the [member x] axis (pitch). - - The [member Vector3.y] should contain the angle around the [member y] axis (yaw). + - The [member Vector3.x] should contain the angle around the [member x] axis (pitch); + - The [member Vector3.y] should contain the angle around the [member y] axis (yaw); - The [member Vector3.z] should contain the angle around the [member z] axis (roll). [codeblocks] [gdscript] # Creates a Basis whose z axis points down. var my_basis = Basis.from_euler(Vector3(TAU / 4, 0, 0)) - print(my_basis.z) # Prints (0, -1, 0). + print(my_basis.z) # Prints (0, -1, 0) [/gdscript] [csharp] // Creates a Basis whose z axis points down. var myBasis = Basis.FromEuler(new Vector3(Mathf.Tau / 4.0f, 0.0f, 0.0f)); - GD.Print(myBasis.Z); // Prints (0, -1, 0). + GD.Print(myBasis.Z); // Prints (0, -1, 0) [/csharp] [/codeblocks] The order of each consecutive rotation can be changed with [param order] (see [enum EulerOrder] constants). By default, the YXZ convention is used ([constant EULER_ORDER_YXZ]): the basis rotates first around the Y axis (yaw), then X (pitch), and lastly Z (roll). When using the opposite method [method get_euler], this order is reversed. @@ -107,16 +112,16 @@ [gdscript] var my_basis = Basis.from_scale(Vector3(2, 4, 8)) - print(my_basis.x) # Prints (2, 0, 0). - print(my_basis.y) # Prints (0, 4, 0). - print(my_basis.z) # Prints (0, 0, 8). + print(my_basis.x) # Prints (2, 0, 0) + print(my_basis.y) # Prints (0, 4, 0) + print(my_basis.z) # Prints (0, 0, 8) [/gdscript] [csharp] var myBasis = Basis.FromScale(new Vector3(2.0f, 4.0f, 8.0f)); - GD.Print(myBasis.X); // Prints (2, 0, 0). - GD.Print(myBasis.Y); // Prints (0, 4, 0). - GD.Print(myBasis.Z); // Prints (0, 0, 8). + GD.Print(myBasis.X); // Prints (2, 0, 0) + GD.Print(myBasis.Y); // Prints (0, 4, 0) + GD.Print(myBasis.Z); // Prints (0, 0, 8) [/csharp] [/codeblocks] [b]Note:[/b] In linear algebra, the matrix of this basis is also known as a [url=https://en.wikipedia.org/wiki/Diagonal_matrix]diagonal matrix[/url]. @@ -126,11 +131,12 @@ - Returns this basis's rotation as a [Vector3] of [url=https://en.wikipedia.org/wiki/Euler_angles]Euler angles[/url], in radians. + Returns this basis's rotation as a [Vector3] of [url=https://en.wikipedia.org/wiki/Euler_angles]Euler angles[/url], in radians. For the returned value: - The [member Vector3.x] contains the angle around the [member x] axis (pitch); - The [member Vector3.y] contains the angle around the [member y] axis (yaw); - The [member Vector3.z] contains the angle around the [member z] axis (roll). The order of each consecutive rotation can be changed with [param order] (see [enum EulerOrder] constants). By default, the YXZ convention is used ([constant EULER_ORDER_YXZ]): Z (roll) is calculated first, then X (pitch), and lastly Y (yaw). When using the opposite method [method from_euler], this order is reversed. + [b]Note:[/b] For this method to return correctly, the basis needs to be [i]orthonormal[/i] (see [method orthonormalized]). [b]Note:[/b] Euler angles are much more intuitive but are not suitable for 3D math. Because of this, consider using the [method get_rotation_quaternion] method instead, which returns a [Quaternion]. [b]Note:[/b] In the Inspector dock, a basis's rotation is often displayed in Euler angles (in degrees), as is the case with the [member Node3D.rotation] property. @@ -145,7 +151,7 @@ - Returns the length of each axis of this basis, as a [Vector3]. If the basis is not sheared, this is the scaling factor. It is not affected by rotation. + Returns the length of each axis of this basis, as a [Vector3]. If the basis is not sheared, this value is the scaling factor. It is not affected by rotation. [codeblocks] [gdscript] var my_basis = Basis( @@ -157,7 +163,7 @@ my_basis = my_basis.rotated(Vector3.UP, TAU / 2) my_basis = my_basis.rotated(Vector3.RIGHT, TAU / 4) - print(my_basis.get_scale()) # Prints (2, 4, 8). + print(my_basis.get_scale()) # Prints (2, 4, 8) [/gdscript] [csharp] var myBasis = new Basis( @@ -169,7 +175,7 @@ myBasis = myBasis.Rotated(Vector3.Up, Mathf.Tau / 2.0f); myBasis = myBasis.Rotated(Vector3.Right, Mathf.Tau / 4.0f); - GD.Print(myBasis.Scale); // Prints (2, 4, 8). + GD.Print(myBasis.Scale); // Prints (2, 4, 8) [/csharp] [/codeblocks] [b]Note:[/b] If the value returned by [method determinant] is negative, the scale is also negative. @@ -214,7 +220,7 @@ - Returns the orthonormalized version of this basis. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1[/code]), which also means it can only represent rotation. + Returns the orthonormalized version of this basis. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1.0[/code]), which also means it can only represent a rotation. It is often useful to call this method to avoid rounding errors on a rotating basis: [codeblocks] [gdscript] @@ -242,8 +248,8 @@ - Returns this basis rotated around the given [param axis] by [param angle] (in radians). The [param axis] must be a normalized vector (see [method Vector3.normalized]). - Positive values rotate this basis clockwise around the axis, while negative values rotate it counterclockwise. + Returns a copy of this basis rotated around the given [param axis] by the given [param angle] (in radians). + The [param axis] must be a normalized vector (see [method Vector3.normalized]). If [param angle] is positive, the basis is rotated counter-clockwise around the axis. [codeblocks] [gdscript] var my_basis = Basis.IDENTITY @@ -279,9 +285,9 @@ ) my_basis = my_basis.scaled(Vector3(0, 2, -2)) - print(my_basis.x) # Prints (0, 2, -2). - print(my_basis.y) # Prints (0, 4, -4). - print(my_basis.z) # Prints (0, 6, -6). + print(my_basis.x) # Prints (0, 2, -2) + print(my_basis.y) # Prints (0, 4, -4) + print(my_basis.z) # Prints (0, 6, -6) [/gdscript] [csharp] var myBasis = new Basis( @@ -291,9 +297,9 @@ ); myBasis = myBasis.Scaled(new Vector3(0.0f, 2.0f, -2.0f)); - GD.Print(myBasis.X); // Prints (0, 2, -2). - GD.Print(myBasis.Y); // Prints (0, 4, -4). - GD.Print(myBasis.Z); // Prints (0, 6, -6). + GD.Print(myBasis.X); // Prints (0, 2, -2) + GD.Print(myBasis.Y); // Prints (0, 4, -4) + GD.Print(myBasis.Z); // Prints (0, 6, -6) [/csharp] [/codeblocks] @@ -354,9 +360,9 @@ ) my_basis = my_basis.transposed() - print(my_basis.x) # Prints (1, 4, 7). - print(my_basis.y) # Prints (2, 5, 8). - print(my_basis.z) # Prints (3, 6, 9). + print(my_basis.x) # Prints (1, 4, 7) + print(my_basis.y) # Prints (2, 5, 8) + print(my_basis.z) # Prints (3, 6, 9) [/gdscript] [csharp] var myBasis = new Basis( @@ -366,9 +372,9 @@ ); myBasis = myBasis.Transposed(); - GD.Print(myBasis.X); // Prints (1, 4, 7). - GD.Print(myBasis.Y); // Prints (2, 5, 8). - GD.Print(myBasis.Z); // Prints (3, 6, 9). + GD.Print(myBasis.X); // Prints (1, 4, 7) + GD.Print(myBasis.Y); // Prints (2, 5, 8) + GD.Print(myBasis.Z); // Prints (3, 6, 9) [/csharp] [/codeblocks] @@ -390,23 +396,24 @@ - The identity basis. This is a basis with no rotation, no shear, and its scale being [code]1[/code]. This means that: + The identity [Basis]. This is an orthonormal basis with no rotation, no shear, and a scale of [constant Vector3.ONE]. This also means that: - The [member x] points right ([constant Vector3.RIGHT]); - The [member y] points up ([constant Vector3.UP]); - The [member z] points back ([constant Vector3.BACK]). [codeblock] - var basis := Basis.IDENTITY + var basis = Basis.IDENTITY print("| X | Y | Z") - print("| %s | %s | %s" % [basis.x.x, basis.y.x, basis.z.x]) - print("| %s | %s | %s" % [basis.x.y, basis.y.y, basis.z.y]) - print("| %s | %s | %s" % [basis.x.z, basis.y.z, basis.z.z]) + print("| %.f | %.f | %.f" % [basis.x.x, basis.y.x, basis.z.x]) + print("| %.f | %.f | %.f" % [basis.x.y, basis.y.y, basis.z.y]) + print("| %.f | %.f | %.f" % [basis.x.z, basis.y.z, basis.z.z]) # Prints: # | X | Y | Z # | 1 | 0 | 0 # | 0 | 1 | 0 # | 0 | 0 | 1 [/codeblock] - This is identical to creating [constructor Basis] without any parameters. This constant can be used to make your code clearer, and for consistency with C#. + If a [Vector3] or another [Basis] is transformed (multiplied) by this constant, no transformation occurs. + [b]Note:[/b] In GDScript, this constant is equivalent to creating a [constructor Basis] without any arguments. It can be used to make your code clearer, and for consistency with C#. When any basis is multiplied by [constant FLIP_X], it negates all components of the [member x] axis (the X column). diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index e8fa13fd0d2..be469080ed1 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -57,7 +57,7 @@ - Sets the [Curve] of the parameter specified by [enum Parameter]. + Sets the [Curve] of the parameter specified by [enum Parameter]. Should be a unit [Curve]. @@ -90,7 +90,7 @@ Number of particles emitted in one emission cycle. - Each particle's rotation will be animated along this [Curve]. + Each particle's rotation will be animated along this [Curve]. Should be a unit [Curve]. Maximum initial rotation applied to each particle, in degrees. @@ -99,7 +99,7 @@ Minimum equivalent of [member angle_max]. - Each particle's angular velocity will vary along this [Curve]. + Each particle's angular velocity will vary along this [Curve]. Should be a unit [Curve]. Maximum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second. @@ -108,7 +108,7 @@ Minimum equivalent of [member angular_velocity_max]. - Each particle's animation offset will vary along this [Curve]. + Each particle's animation offset will vary along this [Curve]. Should be a unit [Curve]. Maximum animation offset that corresponds to frame index in the texture. [code]0[/code] is the first frame, [code]1[/code] is the last one. See [member CanvasItemMaterial.particles_animation]. @@ -117,7 +117,7 @@ Minimum equivalent of [member anim_offset_max]. - Each particle's animation speed will vary along this [Curve]. + Each particle's animation speed will vary along this [Curve]. Should be a unit [Curve]. Maximum particle animation speed. Animation speed of [code]1[/code] means that the particles will make full [code]0[/code] to [code]1[/code] offset cycle during lifetime, [code]2[/code] means [code]2[/code] cycles etc. @@ -136,7 +136,7 @@ Each particle's color will vary along this [Gradient] (multiplied with [member color]). - Damping will vary along this [Curve]. + Damping will vary along this [Curve]. Should be a unit [Curve]. The maximum rate at which particles lose velocity. For example value of [code]100[/code] means that the particle will go from [code]100[/code] velocity to [code]0[/code] in [code]1[/code] second. @@ -184,7 +184,7 @@ Gravity applied to every particle. - Each particle's hue will vary along this [Curve]. + Each particle's hue will vary along this [Curve]. Should be a unit [Curve]. Maximum initial hue variation applied to each particle. It will shift the particle color's hue. @@ -205,7 +205,7 @@ Particle lifetime randomness ratio. - Each particle's linear acceleration will vary along this [Curve]. + Each particle's linear acceleration will vary along this [Curve]. Should be a unit [Curve]. Maximum linear acceleration applied to each particle in the direction of motion. @@ -220,7 +220,7 @@ If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. - Each particle's orbital velocity will vary along this [Curve]. + Each particle's orbital velocity will vary along this [Curve]. Should be a unit [Curve]. Maximum orbital velocity applied to each particle. Makes the particles circle around origin. Specified in number of full rotations around origin per second. @@ -235,7 +235,7 @@ Particle system starts as if it had already run for this many seconds. - Each particle's radial acceleration will vary along this [Curve]. + Each particle's radial acceleration will vary along this [Curve]. Should be a unit [Curve]. Maximum radial acceleration applied to each particle. Makes particle accelerate away from the origin or towards it if negative. @@ -247,7 +247,7 @@ Emission lifetime randomness ratio. - Each particle's scale will vary along this [Curve]. + Each particle's scale will vary along this [Curve]. Should be a unit [Curve]. Maximum initial scale applied to each particle. @@ -256,11 +256,11 @@ Minimum equivalent of [member scale_amount_max]. - Each particle's horizontal scale will vary along this [Curve]. + Each particle's horizontal scale will vary along this [Curve]. Should be a unit [Curve]. [member split_scale] must be enabled. - Each particle's vertical scale will vary along this [Curve]. + Each particle's vertical scale will vary along this [Curve]. Should be a unit [Curve]. [member split_scale] must be enabled. @@ -273,7 +273,7 @@ Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. - Each particle's tangential acceleration will vary along this [Curve]. + Each particle's tangential acceleration will vary along this [Curve]. Should be a unit [Curve]. Maximum tangential acceleration applied to each particle. Tangential acceleration is perpendicular to the particle's velocity giving the particles a swirling motion. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index 04ee95457c4..805299556a8 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -63,7 +63,7 @@ - Sets the [Curve] of the parameter specified by [enum Parameter]. + Sets the [Curve] of the parameter specified by [enum Parameter]. Should be a unit [Curve]. @@ -96,7 +96,7 @@ Number of particles emitted in one emission cycle. - Each particle's rotation will be animated along this [Curve]. + Each particle's rotation will be animated along this [Curve]. Should be a unit [Curve]. Maximum angle. @@ -105,7 +105,7 @@ Minimum angle. - Each particle's angular velocity (rotation speed) will vary along this [Curve] over its lifetime. + Each particle's angular velocity (rotation speed) will vary along this [Curve] over its lifetime. Should be a unit [Curve]. Maximum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second. @@ -114,7 +114,7 @@ Minimum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second. - Each particle's animation offset will vary along this [Curve]. + Each particle's animation offset will vary along this [Curve]. Should be a unit [Curve]. Maximum animation offset. @@ -123,7 +123,7 @@ Minimum animation offset. - Each particle's animation speed will vary along this [Curve]. + Each particle's animation speed will vary along this [Curve]. Should be a unit [Curve]. Maximum particle animation speed. @@ -144,7 +144,7 @@ [b]Note:[/b] [member color_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_ramp] will have no visible effect. - Damping will vary along this [Curve]. + Damping will vary along this [Curve]. Should be a unit [Curve]. Maximum damping. @@ -212,7 +212,7 @@ Gravity applied to every particle. - Each particle's hue will vary along this [Curve]. + Each particle's hue will vary along this [Curve]. Should be a unit [Curve]. Maximum hue variation. @@ -233,7 +233,7 @@ Particle lifetime randomness ratio. - Each particle's linear acceleration will vary along this [Curve]. + Each particle's linear acceleration will vary along this [Curve]. Should be a unit [Curve]. Maximum linear acceleration. @@ -251,7 +251,7 @@ If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end. - Each particle's orbital velocity will vary along this [Curve]. + Each particle's orbital velocity will vary along this [Curve]. Should be a unit [Curve]. Maximum orbit velocity. @@ -272,7 +272,7 @@ Particle system starts as if it had already run for this many seconds. - Each particle's radial acceleration will vary along this [Curve]. + Each particle's radial acceleration will vary along this [Curve]. Should be a unit [Curve]. Maximum radial acceleration. @@ -284,7 +284,7 @@ Emission lifetime randomness ratio. - Each particle's scale will vary along this [Curve]. + Each particle's scale will vary along this [Curve]. Should be a unit [Curve]. Maximum scale. @@ -311,7 +311,7 @@ Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. Applied to X/Z plane and Y/Z planes. - Each particle's tangential acceleration will vary along this [Curve]. + Each particle's tangential acceleration will vary along this [Curve]. Should be a unit [Curve]. Maximum tangent acceleration. diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index 597ec780899..e542eb7aee4 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -45,8 +45,8 @@ The custom [Viewport] node assigned to the [CanvasLayer]. If [code]null[/code], uses the default viewport instead. - If enabled, the [CanvasLayer] will use the viewport's transform, so it will move when camera moves instead of being anchored in a fixed position on the screen. - Together with [member follow_viewport_scale] it can be used for a pseudo 3D effect. + If enabled, the [CanvasLayer] stays in a fixed position on the screen. If disabled, the [CanvasLayer] maintains its position in world space. + Together with [member follow_viewport_scale], this can be used for a pseudo-3D effect. Scales the layer when using [member follow_viewport_enabled]. Layers moving into the foreground should have increasing scales, while layers moving into the background should have decreasing scales. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index a3e999501cf..212fac5eafa 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -6,6 +6,7 @@ Base class for all UI-related nodes. [Control] features a bounding rectangle that defines its extents, an anchor position relative to its parent control or the current viewport, and offsets relative to the anchor. The offsets update automatically when the node, any of its parents, or the screen size change. For more information on Redot's UI system, anchors, offsets, and containers, see the related tutorials in the manual. To build flexible UIs, you'll need a mix of UI elements that inherit from [Control] and [Container] nodes. + [b]Note:[/b] Since both [Node2D] and [Control] inherit from [CanvasItem], they share several concepts from the class such as the [member CanvasItem.z_index] and [member CanvasItem.visible] properties. [b]User Interface nodes and input[/b] Redot propagates input events via viewports. Each [Viewport] is responsible for propagating [InputEvent]s to their child nodes. As the [member SceneTree.root] is a [Window], this already happens automatically for all UI elements in your game. Input events are propagated through the [SceneTree] from the root node to all child nodes by calling [method Node._input]. For UI elements specifically, it makes more sense to override the virtual method [method _gui_input], which filters out unrelated input events, such as by checking z-order, [member mouse_filter], focus, or if the event was inside of the control's bounding box. diff --git a/doc/classes/CubemapArray.xml b/doc/classes/CubemapArray.xml index 935126ba5be..4bed756bce0 100644 --- a/doc/classes/CubemapArray.xml +++ b/doc/classes/CubemapArray.xml @@ -7,7 +7,7 @@ [CubemapArray]s are made of an array of [Cubemap]s. Like [Cubemap]s, they are made of multiple textures, the amount of which must be divisible by 6 (one for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray]. [Cubemap]s are allocated in adjacent cache regions on the GPU, which makes [CubemapArray]s the most efficient way to store multiple [Cubemap]s. [b]Note:[/b] Redot uses [CubemapArray]s internally for many effects, including the [Sky] if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code]. To create such a texture file yourself, reimport your image files using the import presets of the File System dock. - [b]Note:[/b] [CubemapArray] is not supported in the OpenGL 3 rendering backend. + [b]Note:[/b] [CubemapArray] is not supported in the Compatibility renderer. diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 25b06f10638..5246e0ba670 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -4,8 +4,8 @@ A mathematical curve. - This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position. - See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D]. + This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the X and Y axes, but these ranges can be changed. + Please note that many resources and nodes assume they are given [i]unit curves[/i]. A unit curve is a curve whose domain (the X axis) is between [code]0[/code] and [code]1[/code]. Some examples of unit curve usage are [member CPUParticles2D.angle_curve] and [member Line2D.width_curve]. @@ -39,6 +39,12 @@ Removes all points from the curve. + + + + Returns the difference between [member min_domain] and [member max_domain]. + + @@ -74,6 +80,12 @@ Returns the right tangent angle (in degrees) for the point at [param index]. + + + + Returns the difference between [member min_value] and [member max_value]. + + @@ -148,17 +160,28 @@ The number of points to include in the baked (i.e. cached) curve data. + + The maximum domain (x-coordinate) that points can have. + - The maximum value the curve can reach. + The maximum value (y-coordinate) that points can have. Tangents can cause higher values between points. + + + The minimum domain (x-coordinate) that points can have. - The minimum value the curve can reach. + The minimum value (y-coordinate) that points can have. Tangents can cause lower values between points. The number of points describing the curve. + + + Emitted when [member max_domain] or [member min_domain] is changed. + + Emitted when [member max_value] or [member min_value] is changed. diff --git a/doc/classes/CurveTexture.xml b/doc/classes/CurveTexture.xml index 8cb2384da3b..ed4a2f2d357 100644 --- a/doc/classes/CurveTexture.xml +++ b/doc/classes/CurveTexture.xml @@ -4,14 +4,14 @@ A 1D texture where pixel brightness corresponds to points on a curve. - A 1D texture where pixel brightness corresponds to points on a [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files. + A 1D texture where pixel brightness corresponds to points on a unit [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files. If you need to store up to 3 curves within a single texture, use [CurveXYZTexture] instead. See also [GradientTexture1D] and [GradientTexture2D]. - The [Curve] that is rendered onto the texture. + The [Curve] that is rendered onto the texture. Should be a unit [Curve]. diff --git a/doc/classes/CurveXYZTexture.xml b/doc/classes/CurveXYZTexture.xml index 8353ed90920..472ce7bb99a 100644 --- a/doc/classes/CurveXYZTexture.xml +++ b/doc/classes/CurveXYZTexture.xml @@ -4,20 +4,20 @@ A 1D texture where the red, green, and blue color channels correspond to points on 3 curves. - A 1D texture where the red, green, and blue color channels correspond to points on 3 [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files. + A 1D texture where the red, green, and blue color channels correspond to points on 3 unit [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files. If you only need to store one curve within a single texture, use [CurveTexture] instead. See also [GradientTexture1D] and [GradientTexture2D]. - The [Curve] that is rendered onto the texture's red channel. + The [Curve] that is rendered onto the texture's red channel. Should be a unit [Curve]. - The [Curve] that is rendered onto the texture's green channel. + The [Curve] that is rendered onto the texture's green channel. Should be a unit [Curve]. - The [Curve] that is rendered onto the texture's blue channel. + The [Curve] that is rendered onto the texture's blue channel. Should be a unit [Curve]. diff --git a/doc/classes/DTLSServer.xml b/doc/classes/DTLSServer.xml index 41e241779a9..5b626467181 100644 --- a/doc/classes/DTLSServer.xml +++ b/doc/classes/DTLSServer.xml @@ -11,8 +11,8 @@ # server_node.gd extends Node - var dtls := DTLSServer.new() - var server := UDPServer.new() + var dtls = DTLSServer.new() + var server = UDPServer.new() var peers = [] func _ready(): @@ -90,8 +90,8 @@ # client_node.gd extends Node - var dtls := PacketPeerDTLS.new() - var udp := PacketPeerUDP.new() + var dtls = PacketPeerDTLS.new() + var udp = PacketPeerUDP.new() var connected = false func _ready(): diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml index 0f5844fd635..cee4d1fe7ac 100644 --- a/doc/classes/DirAccess.xml +++ b/doc/classes/DirAccess.xml @@ -105,6 +105,17 @@ [b]Note:[/b] This method is implemented on macOS, Linux, and Windows. + + + + + + Creates a temporary directory. This directory will be freed when the returned [DirAccess] is freed. + If [param prefix] is not empty, it will be prefixed to the directory name, separated by a [code]-[/code]. + If [param keep] is [code]true[/code], the directory is not deleted when the returned [DirAccess] is freed. + Returns [code]null[/code] if opening the directory failed. You can use [method get_open_error] to check the error that occurred. + + @@ -221,6 +232,14 @@ Returns the available space on the current directory's disk, in bytes. Returns [code]0[/code] if the platform-specific method to query the available space fails. + + + + + Returns [code]true[/code] if the directory is a macOS bundle. + [b]Note:[/b] This method is implemented on macOS. + + diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 7d954e2aa0b..6fdbb320a79 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -10,6 +10,13 @@ + + + + Plays the beep sound from the operative system, if possible. Because it comes from the OS, the beep sound will be audible even if the application is muted. It may also be disabled for the entire OS by the user. + [b]Note:[/b] This method is implemented on macOS, Linux (X11/Wayland), and Windows. + + @@ -1815,6 +1822,14 @@ [b]Warning:[/b] Advanced users only! Adding such a callback to a [Window] node will override its default implementation, which can introduce bugs. + + + + + Starts a drag operation on the window with the given [param window_id], using the current mouse position. Call this method when handling a mouse button being pressed to simulate a pressed event on the window's title bar. Using this method allows the window to participate in space switching, tiling, and other system features. + [b]Note:[/b] This method is implemented only on macOS. + + @@ -1895,6 +1910,9 @@ The display server supports all features of [constant FEATURE_NATIVE_DIALOG_FILE], with the added functionality of Options and native dialog file access to [code]res://[/code] and [code]user://[/code] paths. See [method file_dialog_show] and [method file_dialog_with_options_show]. [b]Windows, macOS, Linux (X11/Wayland)[/b] + + The display server supports initiating window drag operation on demand. See [method window_start_drag]. + Makes the mouse cursor visible if it is hidden. @@ -2170,12 +2188,12 @@ Window view: - - Windows: [code]HDC[/code] for the window (only with the GL Compatibility renderer). + - Windows: [code]HDC[/code] for the window (only with the Compatibility renderer). - macOS: [code]NSView*[/code] for the window main view. - iOS: [code]UIView*[/code] for the window main view. - OpenGL context (only with the GL Compatibility renderer): + OpenGL context (only with the Compatibility renderer): - Windows: [code]HGLRC[/code] for the window (native GL), or [code]EGLContext[/code] for the window (ANGLE). - Linux (X11): [code]GLXContext*[/code] for the window. - Linux (Wayland): [code]EGLContext[/code] for the window. diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml index 12a7a10d8f5..507209550c1 100644 --- a/doc/classes/EditorExportPlatform.xml +++ b/doc/classes/EditorExportPlatform.xml @@ -125,6 +125,12 @@ Returns array of core file names that always should be exported regardless of preset config. + + + + Returns additional files that should always be exported regardless of preset configuration, and are not part of the project source. The returned [Dictionary] contains filename keys ([String]) and their corresponding raw data ([PackedByteArray]). + + diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index d5c2ed55d75..f2a87a03169 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -37,10 +37,22 @@ Adds the given [param menu] to the side of the file dialog with the given [param title] text on top. Only one side menu is allowed. + + + + Clear the filter for file names. + + - Removes all filters except for "All Files (*)". + Removes all filters except for "All Files (*.*)". + + + + + + Returns the value of the filter for file names. @@ -96,6 +108,13 @@ Shows the [EditorFileDialog] at the default size and position for file dialogs in the editor, and selects the file name if there is a current file. + + + + + Sets the value of the filter for file names. + + @@ -168,6 +187,12 @@ Emitted when a file is selected. + + + + Emitted when the filter for file names changes. + + diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 6b25be490ed..0b56060bda7 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -28,6 +28,8 @@ + + diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index d422bd9b3cd..747260745eb 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -188,6 +188,9 @@ If [code]true[/code], automatically switches to the [b]Remote[/b] scene tree when running the project from the editor. If [code]false[/code], stays on the [b]Local[/b] scene tree when running the project from the editor. [b]Warning:[/b] Enabling this setting can cause stuttering when running a project with a large amount of nodes (typically a few thousands of nodes or more), even if the editor window isn't focused. This is due to the remote scene tree being updated every second regardless of whether the editor is focused. + + If [code]true[/code], automatically switches to the [b]Stack Trace[/b] panel when the debugger hits a breakpoint or steps. + If [code]true[/code], enables collection of profiling data from non-GDScript Redot functions, such as engine class methods. Enabling this slows execution while profiling further. @@ -1031,7 +1034,7 @@ If [code]true[/code], set accent color based on system settings. - [b]Note:[/b] This setting is only effective on Windows and MacOS. + [b]Note:[/b] This setting is only effective on Windows, MacOS, and Android. If [code]true[/code], long press on touchscreen is treated as right click. diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index e3de82dc10d..de7249fafe4 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -50,6 +50,20 @@ [b]Note:[/b] [FileAccess] will automatically close when it's freed, which happens when it goes out of scope or when it gets assigned with [code]null[/code]. In C# the reference must be disposed after we are done using it, this can be done with the [code]using[/code] statement or calling the [code]Dispose[/code] method directly. + + + + + + + + Creates a temporary file. This file will be freed when the returned [FileAccess] is freed. + If [param prefix] is not empty, it will be prefixed to the file name, separated by a [code]-[/code]. + If [param extension] is not empty, it will be appended to the temporary file name. + If [param keep] is [code]true[/code], the file is not deleted when the returned [FileAccess] is freed. + Returns [code]null[/code] if opening the file failed. You can use [method get_open_error] to check the error that occurred. + + @@ -381,20 +395,22 @@ - + Stores an integer as 8 bits in the file. [b]Note:[/b] The [param value] should lie in the interval [code][0, 255][/code]. Any other value will overflow and wrap around. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. To store a signed integer, use [method store_64], or convert it manually (see [method store_16] for an example). - + Stores an integer as 16 bits in the file. [b]Note:[/b] The [param value] should lie in the interval [code][0, 2^16 - 1][/code]. Any other value will overflow and wrap around. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. To store a signed integer, use [method store_64] or store a signed integer from the interval [code][-2^15, 2^15 - 1][/code] (i.e. keeping one bit for the signedness) and compute its sign manually when reading. For example: [codeblocks] [gdscript] @@ -431,97 +447,108 @@ - + Stores an integer as 32 bits in the file. [b]Note:[/b] The [param value] should lie in the interval [code][0, 2^32 - 1][/code]. Any other value will overflow and wrap around. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. To store a signed integer, use [method store_64], or convert it manually (see [method store_16] for an example). - + Stores an integer as 64 bits in the file. [b]Note:[/b] The [param value] must lie in the interval [code][-2^63, 2^63 - 1][/code] (i.e. be a valid [int] value). + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores the given array of bytes in the file. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Store the given [PackedStringArray] in the file as a line formatted in the CSV (Comma-Separated Values) format. You can pass a different delimiter [param delim] to use other than the default [code]","[/code] (comma). This delimiter must be one-character long. Text will be encoded as UTF-8. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores a floating-point number as 64 bits in the file. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores a floating-point number as 32 bits in the file. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores a half-precision floating-point number as 16 bits in the file. - + Stores [param line] in the file followed by a newline character ([code]\n[/code]), encoding the text as UTF-8. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores the given [String] as a line in the file in Pascal format (i.e. also store the length of the string). Text will be encoded as UTF-8. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores a floating-point number in the file. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores [param string] in the file without a newline character ([code]\n[/code]), encoding the text as UTF-8. [b]Note:[/b] This method is intended to be used to write text files. The string is stored as a UTF-8 encoded buffer without string length or terminating zero, which means that it can't be loaded back easily. If you want to store a retrievable string in a binary file, consider using [method store_pascal_string] instead. For retrieving strings from a text file, you can use [code]get_buffer(length).get_string_from_utf8()[/code] (if you know the length) or [method get_as_text]. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. - + Stores any Variant value in the file. If [param full_objects] is [code]true[/code], encoding objects is allowed (and can potentially include code). Internally, this uses the same encoding mechanism as the [method @GlobalScope.var_to_bytes] method. [b]Note:[/b] Not all properties are included. Only properties that are configured with the [constant PROPERTY_USAGE_STORAGE] flag set will be serialized. You can add a new usage flag to a property by overriding the [method Object._get_property_list] method in your class. You can also check how property usage is configured by calling [method Object._get_property_list]. See [enum PropertyUsageFlags] for the possible usage flags. + [b]Note:[/b] If an error occurs, the resulting value of the file position indicator is indeterminate. diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml index c230bf5ad28..e7a724cecd4 100644 --- a/doc/classes/FontFile.xml +++ b/doc/classes/FontFile.xml @@ -631,6 +631,9 @@ Font hinting mode. Used by dynamic fonts only. + + If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + The width of the range around the shape between the minimum and maximum representable signed distance. If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. The default [member msdf_pixel_range] value of [code]16[/code] allows outline sizes up to [code]8[/code] to look correct. diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 3451e427f34..bd0ea02eeb0 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -181,6 +181,14 @@ [/codeblocks] + + + + + + Returns the number of connections from [param from_port] of [param from_node]. + + diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index ae5a62753f1..7657cf31214 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -74,6 +74,9 @@ Limits the lines of text the node shows on screen. + + String used as a paragraph separator. Each paragraph is processed independently, in its own BiDi context. + Set BiDi algorithm override for the structured text. @@ -129,6 +132,9 @@ [b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended. [b]Note:[/b] Using a value that is larger than half the font size is not recommended, as the font outline may fail to be fully closed in this case. + + Vertical space between paragraphs. Added on top of [theme_item line_spacing]. + The horizontal offset of the text's shadow. diff --git a/doc/classes/LabelSettings.xml b/doc/classes/LabelSettings.xml index ff7b8e7b0e4..610f10574b5 100644 --- a/doc/classes/LabelSettings.xml +++ b/doc/classes/LabelSettings.xml @@ -27,6 +27,9 @@ Text outline size. + + Vertical space between paragraphs. Added on top of [member line_spacing]. + Color of the shadow effect. If alpha is [code]0[/code], no shadow will be drawn. diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index 0d471b579dd..a46a0e3d5e9 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -9,7 +9,7 @@ [b]Performance:[/b] [LightmapGI] provides the best possible run-time performance for global illumination. It is suitable for low-end hardware including integrated graphics and mobile devices. [b]Note:[/b] Due to how lightmaps work, most properties only have a visible effect once lightmaps are baked again. [b]Note:[/b] Lightmap baking on [CSGShape3D]s and [PrimitiveMesh]es is not supported, as these cannot store UV2 data required for baking. - [b]Note:[/b] If no custom lightmappers are installed, [LightmapGI] can only be baked from devices that support the Forward+ or Mobile rendering backends. + [b]Note:[/b] If no custom lightmappers are installed, [LightmapGI] can only be baked from devices that support the Forward+ or Mobile renderers. [b]Note:[/b] The [LightmapGI] node only bakes light data for child nodes of its parent. Nodes further up the hierarchy of the scene will not be baked. diff --git a/doc/classes/LightmapperRD.xml b/doc/classes/LightmapperRD.xml index 8fe42ad9cd9..c533311b9e2 100644 --- a/doc/classes/LightmapperRD.xml +++ b/doc/classes/LightmapperRD.xml @@ -5,7 +5,7 @@ LightmapperRD ("RD" stands for [RenderingDevice]) is the built-in GPU-based lightmapper for use with [LightmapGI]. On most dedicated GPUs, it can bake lightmaps much faster than most CPU-based lightmappers. LightmapperRD uses compute shaders to bake lightmaps, so it does not require CUDA or OpenCL libraries to be installed to be usable. - [b]Note:[/b] Only usable when using the Vulkan backend (Forward+ or Mobile), not OpenGL. + [b]Note:[/b] Only usable when using the RenderingDevice backend (Forward+ or Mobile renderers), not Compatibility. diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index a553e79746e..825462d21a7 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -101,7 +101,7 @@ The polyline's width. - The polyline's width curve. The width of the polyline over its length will be equivalent to the value of the width curve over its domain. + The polyline's width curve. The width of the polyline over its length will be equivalent to the value of the width curve over its domain. The width curve should be a unit [Curve]. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index f056864f400..903d5ffd303 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -1340,7 +1340,7 @@ Duplicate the node's script (also overriding the duplicated children's scripts, if combined with [constant DUPLICATE_USE_INSTANTIATION]). - Duplicate using [method PackedScene.instantiate]. If the node comes from a scene saved on disk, re-uses [method PackedScene.instantiate] as the base for the duplicated node and its children. + Duplicate using [method PackedScene.instantiate]. If the node comes from a scene saved on disk, reuses [method PackedScene.instantiate] as the base for the duplicated node and its children. The node will not be internal. diff --git a/doc/classes/Node2D.xml b/doc/classes/Node2D.xml index 400dca3bbd6..24e45aa0ec9 100644 --- a/doc/classes/Node2D.xml +++ b/doc/classes/Node2D.xml @@ -1,10 +1,11 @@ - A 2D game object, inherited by all 2D-related nodes. Has a position, rotation, scale, and Z index. + A 2D game object, inherited by all 2D-related nodes. Has a position, rotation, scale, and skew. A 2D game object, with a transform (position, rotation, and scale). All 2D nodes, including physics objects and sprites, inherit from Node2D. Use Node2D as a parent node to move, scale and rotate children in a 2D project. Also gives control of the node's render order. + [b]Note:[/b] Since both [Node2D] and [Control] inherit from [CanvasItem], they share several concepts from the class such as the [member CanvasItem.z_index] and [member CanvasItem.visible] properties. $DOCS_URL/tutorials/2d/custom_drawing_in_2d.html diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index fd2ea8790c6..3d5b6d1a81b 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -537,6 +537,12 @@ [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows. + + + + Returns the [i]global[/i] temporary data directory according to the operating system's standards. + + diff --git a/doc/classes/PCKPacker.xml b/doc/classes/PCKPacker.xml index f8f7dbee01b..dbb978865c6 100644 --- a/doc/classes/PCKPacker.xml +++ b/doc/classes/PCKPacker.xml @@ -20,6 +20,7 @@ [/csharp] [/codeblocks] The above [PCKPacker] creates package [code]test.pck[/code], then adds a file named [code]text.txt[/code] at the root of the package. + [b]Note:[/b] PCK is Godot's own pack file format. To create ZIP archives that can be read by any program, use [ZIPPacker] instead. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index bda9af86b7e..d59703d9837 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2702,7 +2702,7 @@ The maximum number of uniforms that can be used by the global shader uniform buffer. Each item takes up one slot. In other words, a single uniform float and a uniform vec4 will take the same amount of space in the buffer. - [b]Note:[/b] When using the Compatibility backend, most mobile devices (and all web exports) will be limited to a maximum size of 1024 due to hardware constraints. + [b]Note:[/b] When using the Compatibility renderer, most mobile devices (and all web exports) will be limited to a maximum size of 1024 due to hardware constraints. Max number of omnilights and spotlights renderable per object. At the default value of 8, this means that each surface can be affected by up to 8 omnilights and 8 spotlights. This is further limited by hardware support and [member rendering/limits/opengl/max_renderable_lights]. Setting this low will slightly reduce memory usage, may decrease shader compile times, and may result in faster rendering on low-end, mobile, or web devices. @@ -2775,9 +2775,9 @@ Sets the renderer that will be used by the project. Options are: - [b]Forward Plus[/b]: High-end renderer designed for Desktop devices. Has a higher base overhead, but scales well with complex scenes. Not suitable for older devices or mobile. - [b]Mobile[/b]: Modern renderer designed for mobile devices. Has a lower base overhead than Forward Plus, but does not scale as well to large scenes with many elements. - [b]GL Compatibility[/b]: Low-end renderer designed for older devices. Based on the limitations of the OpenGL 3.3/ OpenGL ES 3.0 / WebGL 2 APIs. + [b]forward_plus[/b] (Forward+): High-end renderer designed for desktop devices. Has a higher base overhead, but scales well with complex scenes. Not suitable for older devices or mobile. + [b]mobile[/b] (Mobile): Modern renderer designed for mobile devices. Has a lower base overhead than Forward+, but does not scale as well to large scenes with many elements. + [b]gl_compatibility[/b] (Compatibility): Low-end renderer designed for older devices. Based on the limitations of the OpenGL 3.3 / OpenGL ES 3.0 / WebGL 2 APIs. This can be overridden using the [code]--rendering-method <method>[/code] command line argument. [b]Note:[/b] The actual rendering method may be automatically changed by the engine as a result of a fallback, or a user-specified command line argument. To get the actual rendering method that is used at runtime, use [method RenderingServer.get_current_rendering_method] instead of reading this project setting's value. @@ -2803,7 +2803,7 @@ Depending on the complexity of scenes, this value may be lowered or may need to be raised. - Sets the driver to be used by the renderer when using a RenderingDevice-based renderer like the clustered renderer or the mobile renderer. This property can't be edited directly. Instead, set the driver using the platform-specific overrides. This can be overridden using the [code]--rendering-driver <driver>[/code] command line argument. + Sets the driver to be used by the renderer when using a RenderingDevice-based renderer like the Forward+ or Mobile renderers. This property can't be edited directly. Instead, set the driver using the platform-specific overrides. This can be overridden using the [code]--rendering-driver <driver>[/code] command line argument. [b]Note:[/b] The actual rendering driver may be automatically changed by the engine as a result of a fallback, or a user-specified command line argument. To get the actual rendering driver that is used at runtime, use [method RenderingServer.get_current_rendering_driver_name] instead of reading this project setting's value. @@ -2928,8 +2928,8 @@ If [code]true[/code], the texture importer will utilize the GPU for compressing textures, improving the import time of large images. - [b]Note:[/b] This only functions on a device which supports either Vulkan, D3D12, or Metal available as a rendering backend. - [b]Note:[/b] Currently this only affects certain compressed formats (BC1, BC4, and BC6), all of which are exclusive to desktop platforms and consoles. + [b]Note:[/b] This only functions on a device which supports either Vulkan, Direct3D 12, or Metal as a rendering driver. + [b]Note:[/b] Currently this only affects certain compressed formats (BC1, BC3, BC4, BC5, and BC6), all of which are exclusive to desktop platforms and consoles. If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm for lower quality textures and normal maps and Adaptable Scalable Texture Compression algorithm for high quality textures (in 4×4 block size). @@ -2949,7 +2949,7 @@ If [code]true[/code], enables [member Viewport.use_hdr_2d] on the root viewport. 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be an [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be an [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen. Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. - [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons. + [b]Note:[/b] This setting will have no effect when using the Compatibility renderer, which always renders in low dynamic range for performance reasons. [b]Note:[/b] This property is only read when the project starts. To toggle HDR 2D at runtime, set [member Viewport.use_hdr_2d] on the root [Viewport]. diff --git a/doc/classes/PropertyTweener.xml b/doc/classes/PropertyTweener.xml index 76cf4cbfeb5..08ce876e918 100644 --- a/doc/classes/PropertyTweener.xml +++ b/doc/classes/PropertyTweener.xml @@ -16,10 +16,16 @@ When called, the final value will be used as a relative value instead. [b]Example:[/b] Move the node by [code]100[/code] pixels to the right. - [codeblock] + [codeblocks] + [gdscript] var tween = get_tree().create_tween() tween.tween_property(self, "position", Vector2.RIGHT * 100, 1).as_relative() - [/codeblock] + [/gdscript] + [csharp] + Tween tween = GetTree().CreateTween(); + tween.TweenProperty(this, "position", Vector2.Right * 100.0f, 1.0f).AsRelative(); + [/csharp] + [/codeblocks] @@ -28,20 +34,32 @@ Sets a custom initial value to the [PropertyTweener]. [b]Example:[/b] Move the node from position [code](100, 100)[/code] to [code](200, 100)[/code]. - [codeblock] + [codeblocks] + [gdscript] var tween = get_tree().create_tween() tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100)) - [/codeblock] + [/gdscript] + [csharp] + Tween tween = GetTree().CreateTween(); + tween.TweenProperty(this, "position", new Vector2(200.0f, 100.0f), 1.0f).From(new Vector2(100.0f, 100.0f)); + [/csharp] + [/codeblocks] Makes the [PropertyTweener] use the current property value (i.e. at the time of creating this [PropertyTweener]) as a starting point. This is equivalent of using [method from] with the current value. These two calls will do the same: - [codeblock] + [codeblocks] + [gdscript] tween.tween_property(self, "position", Vector2(200, 100), 1).from(position) tween.tween_property(self, "position", Vector2(200, 100), 1).from_current() - [/codeblock] + [/gdscript] + [csharp] + tween.TweenProperty(this, "position", new Vector2(200.0f, 100.0f), 1.0f).From(Position); + tween.TweenProperty(this, "position", new Vector2(200.0f, 100.0f), 1.0f).FromCurrent(); + [/csharp] + [/codeblocks] @@ -49,7 +67,8 @@ Allows interpolating the value with a custom easing function. The provided [param interpolator_method] will be called with a value ranging from [code]0.0[/code] to [code]1.0[/code] and is expected to return a value within the same range (values outside the range can be used for overshoot). The return value of the method is then used for interpolation between initial and final value. Note that the parameter passed to the method is still subject to the tweener's own easing. - [codeblock] + [codeblocks] + [gdscript] @export var curve: Curve func _ready(): @@ -59,7 +78,25 @@ func tween_curve(v): return curve.sample_baked(v) - [/codeblock] + [/gdscript] + [csharp] + [Export] + public Curve Curve { get; set; } + + public override void _Ready() + { + Tween tween = CreateTween(); + // Interpolate the value using a custom curve. + Callable tweenCurveCallable = Callable.From<float, float>(TweenCurve); + tween.TweenProperty(this, "position:x", 300.0f, 1.0f).AsRelative().SetCustomInterpolator(tweenCurveCallable); + } + + private float TweenCurve(float value) + { + return Curve.SampleBaked(value); + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 2875ad1eaf3..f51fe8eee07 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -21,7 +21,7 @@ - Constructs a [Quaternion] identical to the [constant IDENTITY]. + Constructs a [Quaternion] identical to [constant IDENTITY]. [b]Note:[/b] In C#, this constructs a [Quaternion] with all of its components set to [code]0.0[/code]. @@ -129,7 +129,7 @@ - Returns [code]true[/code] if this quaternion and [param to] are approximately equal, by running [method @GlobalScope.is_equal_approx] on each component. + Returns [code]true[/code] if this quaternion and [param to] are approximately equal, by calling [method @GlobalScope.is_equal_approx] on each component. @@ -232,6 +232,7 @@ The identity quaternion, representing no rotation. This has the same rotation as [constant Basis.IDENTITY]. If a [Vector3] is rotated (multiplied) by this quaternion, it does not change. + [b]Note:[/b] In GDScript, this constant is equivalent to creating a [constructor Quaternion] without any arguments. It can be used to make your code clearer, and for consistency with C#. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6bd810f5d85..ce74f5f1c41 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1075,7 +1075,7 @@ Creates a RenderingDevice that can be used to do draw and compute operations on a separate thread. Cannot draw to the screen nor share data with the global RenderingDevice. - [b]Note:[/b] When using the OpenGL backend or when running in headless mode, this function always returns [code]null[/code]. + [b]Note:[/b] When using the OpenGL rendering driver or when running in headless mode, this function always returns [code]null[/code]. @@ -1578,7 +1578,7 @@ Returns the global RenderingDevice. - [b]Note:[/b] When using the OpenGL backend or when running in headless mode, this function always returns [code]null[/code]. + [b]Note:[/b] When using the OpenGL rendering driver or when running in headless mode, this function always returns [code]null[/code]. @@ -1641,7 +1641,7 @@ Returns the type of the video adapter. Since dedicated graphics cards from a given generation will [i]usually[/i] be significantly faster than integrated graphics made in the same generation, the device type can be used as a basis for automatic graphics settings adjustment. However, this is not always true, so make sure to provide users with a way to manually override graphics settings. - [b]Note:[/b] When using the OpenGL backend or when running in headless mode, this function always returns [constant RenderingDevice.DEVICE_TYPE_OTHER]. + [b]Note:[/b] When using the OpenGL rendering driver or when running in headless mode, this function always returns [constant RenderingDevice.DEVICE_TYPE_OTHER]. @@ -4121,7 +4121,7 @@ If [code]true[/code], 2D rendering will use a high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be an [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be an [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. This setting has the same effect as [member Viewport.use_hdr_2d]. - [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons. + [b]Note:[/b] This setting will have no effect when using the Compatibility renderer, which always renders in low dynamic range for performance reasons. @@ -5724,7 +5724,7 @@ Buffer memory used (in bytes). This includes vertex data, uniform buffers, and many miscellaneous buffer types used internally. - Video memory used (in bytes). When using the Forward+ or mobile rendering backends, this is always greater than the sum of [constant RENDERING_INFO_TEXTURE_MEM_USED] and [constant RENDERING_INFO_BUFFER_MEM_USED], since there is miscellaneous data not accounted for by those two metrics. When using the GL Compatibility backend, this is equal to the sum of [constant RENDERING_INFO_TEXTURE_MEM_USED] and [constant RENDERING_INFO_BUFFER_MEM_USED]. + Video memory used (in bytes). When using the Forward+ or Mobile renderers, this is always greater than the sum of [constant RENDERING_INFO_TEXTURE_MEM_USED] and [constant RENDERING_INFO_BUFFER_MEM_USED], since there is miscellaneous data not accounted for by those two metrics. When using the Compatibility renderer, this is equal to the sum of [constant RENDERING_INFO_TEXTURE_MEM_USED] and [constant RENDERING_INFO_BUFFER_MEM_USED]. Number of pipeline compilations that were triggered by the 2D canvas renderer. diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml index 3727bed8e5f..ee7504aed89 100644 --- a/doc/classes/ResourceImporterDynamicFont.xml +++ b/doc/classes/ResourceImporterDynamicFont.xml @@ -44,6 +44,9 @@ [b]Light:[/b] Sharp result by snapping glyph edges to pixels on the Y axis only. [b]Full:[/b] Sharpest by snapping glyph edges to pixels on both X and Y axes. + + If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + Override the list of languages supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member script_support]. diff --git a/doc/classes/RibbonTrailMesh.xml b/doc/classes/RibbonTrailMesh.xml index 74523f3c390..113787d3a4e 100644 --- a/doc/classes/RibbonTrailMesh.xml +++ b/doc/classes/RibbonTrailMesh.xml @@ -13,7 +13,7 @@ - Determines the size of the ribbon along its length. The size of a particular section segment is obtained by multiplying the baseline [member size] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted. + Determines the size of the ribbon along its length. The size of a particular section segment is obtained by multiplying the baseline [member size] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted. Should be a unit [Curve]. The length of a section of the ribbon. diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 8472e578401..d75d3043e02 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -5,6 +5,7 @@ A control for displaying text that can contain custom fonts, images, and basic formatting. [RichTextLabel] manages these as an internal tag stack. It also adapts itself to given width/heights. + [b]Note:[/b] [method newline], [method push_paragraph], [code]"\n"[/code], [code]"\r\n"[/code], [code]p[/code] tag, and alignment tags start a new paragraph. Each paragraph is processed independently, in its own BiDi context. If you want to force line wrapping within paragraph, any other line breaking character can be used, for example, Form Feed (U+000C), Next Line (U+0085), Line Separator (U+2028). [b]Note:[/b] Assignments to [member text] clear the tag stack and reconstruct it from the property's contents. Any edits made to [member text] will erase previous edits made from other manual sources such as [method append_text] and the [code]push_*[/code] / [method pop] methods. [b]Note:[/b] RichTextLabel doesn't support entangled BBCode tags. For example, instead of using [code skip-lint][b]bold[i]bold italic[/b]italic[/i][/code], use [code skip-lint][b]bold[i]bold italic[/i][/b][i]italic[/i][/code]. [b]Note:[/b] [code]push_*/pop_*[/code] functions won't affect BBCode. @@ -99,6 +100,7 @@ Returns the total number of lines in the text. Wrapped text is counted as multiple lines. + [b]Note:[/b] If [member visible_characters_behavior] is set to [constant TextServer.VC_CHARS_BEFORE_SHAPING] only visible wrapped lines are counted. [b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_finished] or [signal finished] to determine whether document is fully loaded. @@ -442,6 +444,7 @@ + Adds a meta tag to the tag stack. Similar to the BBCode [code skip-lint][url=something]{text}[/url][/code], but supports non-[String] metadata types. If [member meta_underlined] is [code]true[/code], meta tags display an underline. This behavior can be customized with [param underline_mode]. @@ -681,6 +684,9 @@ If [code]true[/code], text processing is done in a background thread. + + Controls the text's vertical alignment. Supports top, center, bottom, and fill. Set it to one of the [enum VerticalAlignment] constants. + The number of characters to display. If set to [code]-1[/code], all characters are displayed. This can be useful when animating the text appearing in a dialog box. [b]Note:[/b] Setting this property updates [member visible_ratio] accordingly. diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index 405bba35643..959721b115d 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -40,6 +40,9 @@ + + If [code]true[/code], [theme_item focus] is drawn when the ScrollContainer or one of its descendant nodes is focused. + If [code]true[/code], the ScrollContainer will automatically scroll to focused children (including indirect children) to make sure they are fully visible. @@ -107,6 +110,9 @@ + + The focus border [StyleBox] of the [ScrollContainer]. Only used if [member draw_focus_border] is [code]true[/code]. + The background [StyleBox] of the [ScrollContainer]. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 1ff9efdc94e..9247f7ae77d 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -6,7 +6,7 @@ This is the built-in string Variant type (and the one used by GDScript). Strings may contain any number of Unicode characters, and expose methods useful for manipulating and generating strings. Strings are reference-counted and use a copy-on-write approach (every modification to a string returns a new [String]), so passing them around is cheap in resources. Some string methods have corresponding variations. Variations suffixed with [code]n[/code] ([method countn], [method findn], [method replacen], etc.) are [b]case-insensitive[/b] (they make no distinction between uppercase and lowercase letters). Method variations prefixed with [code]r[/code] ([method rfind], [method rsplit], etc.) are reversed, and start from the end of the string, instead of the beginning. - To convert any Variant to or from a string, see [method @GlobalScope.str], [method @GlobalScope.str_to_var], and [method @GlobalScope.var_to_str]. + To convert any [Variant] to or from a string, see [method @GlobalScope.str], [method @GlobalScope.str_to_var], and [method @GlobalScope.var_to_str]. [b]Note:[/b] In a boolean context, a string will evaluate to [code]false[/code] if it is empty ([code]""[/code]). Otherwise, a string will always evaluate to [code]true[/code]. diff --git a/doc/classes/SubViewportContainer.xml b/doc/classes/SubViewportContainer.xml index 310f235abf6..b01a81a61f0 100644 --- a/doc/classes/SubViewportContainer.xml +++ b/doc/classes/SubViewportContainer.xml @@ -20,11 +20,12 @@ - - If [code]false[/code], the [SubViewportContainer] is not available as a drop target in drag-and-drop operations, and instead the [Control] nodes inside its [Viewport] children are potential drop targets. - If [code]true[/code], the [SubViewportContainer] itself will be considered as a drop target in drag-and-drop operations, preventing the [Control] nodes inside its [Viewport] children from becoming drop targets. - + + Configure, if either the [SubViewportContainer] or alternatively the [Control] nodes of its [SubViewport] children should be available as targets of mouse-related functionalities, like identifying the drop target in drag-and-drop operations or cursor shape of hovered [Control] node. + If [code]false[/code], the [Control] nodes inside its [SubViewport] children are considered as targets. + If [code]true[/code], the [SubViewportContainer] itself will be considered as a target. + If [code]true[/code], the sub-viewport will be automatically resized to the control's size. [b]Note:[/b] If [code]true[/code], this will prohibit changing [member SubViewport.size] of its children manually. diff --git a/doc/classes/SystemFont.xml b/doc/classes/SystemFont.xml index 38d6e27c856..b91ae74eae8 100644 --- a/doc/classes/SystemFont.xml +++ b/doc/classes/SystemFont.xml @@ -43,6 +43,9 @@ Font hinting mode. + + If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + The width of the range around the shape between the minimum and maximum representable signed distance. If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. The default [member msdf_pixel_range] value of [code]16[/code] allows outline sizes up to [code]8[/code] to look correct. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 1982840bd4f..21f032d4620 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -317,6 +317,13 @@ Returns the font hinting mode. Used by dynamic fonts only. + + + + + Returns glyph position rounding behavior. If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + + @@ -824,6 +831,14 @@ Sets font hinting mode. Used by dynamic fonts only. + + + + + + Sets glyph position rounding behavior. If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + + @@ -1067,6 +1082,12 @@ Returns the name of the server interface. + + + + Returns default TextServer database (e.g. ICU break iterators and dictionaries). + + @@ -1914,6 +1935,7 @@ Trims text before the shaping. e.g, increasing [member Label.visible_characters] or [member RichTextLabel.visible_characters] value is visually identical to typing the text. + [b]Note:[/b] In this mode, trimmed text is not processed at all. It is not accounted for in line breaking and size calculations. Displays glyphs that are mapped to the first [member Label.visible_characters] or [member RichTextLabel.visible_characters] characters from the beginning of the text. diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 3c27404f8e3..1df451705c3 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -329,6 +329,14 @@ Returns the font hinting mode. Used by dynamic fonts only. + + + + + [b]Optional.[/b] + Returns glyph position rounding behavior. If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + + @@ -904,6 +912,15 @@ Sets font hinting mode. Used by dynamic fonts only. + + + + + + [b]Optional.[/b] + Sets glyph position rounding behavior. If set to [code]true[/code], when aligning glyphs to the pixel boundaries rounding remainders are accumulated to ensure more uniform glyph distribution. This setting has no effect if subpixel positioning is enabled. + + @@ -1170,6 +1187,13 @@ Returns the name of the server interface. + + + + [b]Optional.[/b] + Returns default TextServer database (e.g. ICU break iterators and dictionaries). + + diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml index ec8838bfe19..12e516a97bb 100644 --- a/doc/classes/TextureProgressBar.xml +++ b/doc/classes/TextureProgressBar.xml @@ -35,6 +35,7 @@ Offsets [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. + [b]Note:[/b] The effective radial center always stays within the [member texture_progress] bounds. If you need to move it outside the texture's bounds, modify the [member texture_progress] to contain additional empty space where needed. Upper limit for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]max_value[/code], the texture fills up to this angle. diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml index a34ca0ce91b..684c05f3283 100644 --- a/doc/classes/TileSetAtlasSource.xml +++ b/doc/classes/TileSetAtlasSource.xml @@ -270,7 +270,7 @@ The atlas texture. - The base tile size in the texture (in pixel). This size must be bigger than the TileSet's [code]tile_size[/code] value. + The base tile size in the texture (in pixel). This size must be bigger than or equal to the TileSet's [code]tile_size[/code] value. If [code]true[/code], generates an internal texture with an additional one pixel padding around each tile. Texture padding avoids a common artifact where lines appear between tiles. diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index 665e5e9d675..bdc908d3877 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -62,8 +62,8 @@ - Returns the inverted version of this transform. Unlike [method inverse], this method works with almost any basis, including non-uniform ones, but is slower. See also [method inverse]. - [b]Note:[/b] For this method to return correctly, the transform's basis needs to have a determinant that is not exactly [code]0[/code] (see [method determinant]). + Returns the inverted version of this transform. Unlike [method inverse], this method works with almost any basis, including non-uniform ones, but is slower. + [b]Note:[/b] For this method to return correctly, the transform's basis needs to have a determinant that is not exactly [code]0.0[/code] (see [method determinant]). @@ -85,7 +85,7 @@ Returns the [url=https://en.wikipedia.org/wiki/Determinant]determinant[/url] of this transform basis's matrix. For advanced math, this number can be used to determine a few attributes: - - If the determinant is exactly [code]0[/code], the basis is not invertible (see [method inverse]). + - If the determinant is exactly [code]0.0[/code], the basis is not invertible (see [method inverse]). - If the determinant is a negative number, the basis represents a negative scale. [b]Note:[/b] If the basis's scale is the same for every axis, its determinant is always that scale by the power of 2. @@ -116,7 +116,7 @@ # Rotating the Transform2D in any way preserves its scale. my_transform = my_transform.rotated(TAU / 2) - print(my_transform.get_scale()) # Prints (2, 4). + print(my_transform.get_scale()) # Prints (2, 4) [/gdscript] [csharp] var myTransform = new Transform2D( @@ -127,7 +127,7 @@ // Rotating the Transform2D in any way preserves its scale. myTransform = myTransform.Rotated(Mathf.Tau / 2.0f); - GD.Print(myTransform.GetScale()); // Prints (2, 4, 8). + GD.Print(myTransform.GetScale()); // Prints (2, 4) [/csharp] [/codeblocks] [b]Note:[/b] If the value returned by [method determinant] is negative, the scale is also negative. @@ -152,7 +152,7 @@ Returns the [url=https://en.wikipedia.org/wiki/Invertible_matrix]inverted version of this transform[/url]. - [b]Note:[/b] For this method to return correctly, the transform's basis needs to be [i]orthonormal[/i] (see [method orthonormalized]). That means, the basis should only represent a rotation. If it does not, use [method affine_inverse] instead. + [b]Note:[/b] For this method to return correctly, the transform's basis needs to be [i]orthonormal[/i] (see [method orthonormalized]). That means the basis should only represent a rotation. If it does not, use [method affine_inverse] instead. @@ -184,14 +184,15 @@ - Returns a copy of this transform with its basis orthonormalized. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1[/code]), which also means it can only represent rotation. + Returns a copy of this transform with its basis orthonormalized. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1.0[/code]), which also means it can only represent a rotation. - Returns a copy of the transform rotated by the given [param angle] (in radians). + Returns a copy of this transform rotated by the given [param angle] (in radians). + If [param angle] is positive, the transform is rotated clockwise. This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code]. This can be seen as transforming with respect to the global/parent frame. @@ -257,28 +258,29 @@ - The identity [Transform2D]. A transform with no translation, no rotation, and its scale being [code]1[/code]. When multiplied by another [Variant] such as [Rect2] or another [Transform2D], no transformation occurs. This means that: + The identity [Transform2D]. This is a transform with no translation, no rotation, and a scale of [constant Vector2.ONE]. This also means that: - The [member x] points right ([constant Vector2.RIGHT]); - The [member y] points down ([constant Vector2.DOWN]). [codeblock] var transform = Transform2D.IDENTITY print("| X | Y | Origin") - print("| %s | %s | %s" % [transform.x.x, transform.y.x, transform.origin.x]) - print("| %s | %s | %s" % [transform.x.y, transform.y.y, transform.origin.y]) + print("| %.f | %.f | %.f" % [transform.x.x, transform.y.x, transform.origin.x]) + print("| %.f | %.f | %.f" % [transform.x.y, transform.y.y, transform.origin.y]) # Prints: # | X | Y | Origin # | 1 | 0 | 0 # | 0 | 1 | 0 [/codeblock] - This is identical to creating [constructor Transform2D] without any parameters. This constant can be used to make your code clearer, and for consistency with C#. + If a [Vector2], a [Rect2], a [PackedVector2Array], or another [Transform2D] is transformed (multiplied) by this constant, no transformation occurs. + [b]Note:[/b] In GDScript, this constant is equivalent to creating a [constructor Transform2D] without any arguments. It can be used to make your code clearer, and for consistency with C#. When any transform is multiplied by [constant FLIP_X], it negates all components of the [member x] axis (the X column). - When [constant FLIP_X] is multiplied by any basis, it negates the [member Vector2.x] component of all axes (the X row). + When [constant FLIP_X] is multiplied by any transform, it negates the [member Vector2.x] component of all axes (the X row). When any transform is multiplied by [constant FLIP_Y], it negates all components of the [member y] axis (the Y column). - When [constant FLIP_Y] is multiplied by any basis, it negates the [member Vector2.y] component of all axes (the Y row). + When [constant FLIP_Y] is multiplied by any transform, it negates the [member Vector2.y] component of all axes (the Y row). diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index 22b6b73aa6a..8c95e972ea1 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -20,7 +20,7 @@ - Constructs a [Transform3D] identical to the [constant IDENTITY]. + Constructs a [Transform3D] identical to [constant IDENTITY]. [b]Note:[/b] In C#, this constructs a [Transform3D] with its [member origin] and the components of its [member basis] set to [constant Vector3.ZERO]. @@ -63,7 +63,7 @@ Returns the inverted version of this transform. Unlike [method inverse], this method works with almost any [member basis], including non-uniform ones, but is slower. See also [method Basis.inverse]. - [b]Note:[/b] For this method to return correctly, the transform's [member basis] needs to have a determinant that is not exactly [code]0[/code] (see [method Basis.determinant]). + [b]Note:[/b] For this method to return correctly, the transform's [member basis] needs to have a determinant that is not exactly [code]0.0[/code] (see [method Basis.determinant]). @@ -78,8 +78,8 @@ - Returns the inverted version of this transform. See also [method Basis.inverse]. - [b]Note:[/b] For this method to return correctly, the transform's [member basis] needs to be [i]orthonormal[/i] (see [method Basis.orthonormalized]). That means, the basis should only represent a rotation. If it does not, use [method affine_inverse] instead. + Returns the [url=https://en.wikipedia.org/wiki/Invertible_matrix]inverted version of this transform[/url]. See also [method Basis.inverse]. + [b]Note:[/b] For this method to return correctly, the transform's [member basis] needs to be [i]orthonormal[/i] (see [method orthonormalized]). That means the basis should only represent a rotation. If it does not, use [method affine_inverse] instead. @@ -109,7 +109,7 @@ - Returns a copy of this transform with its [member basis] orthonormalized. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1[/code]), which also means it can only represent rotation. See also [method Basis.orthonormalized]. + Returns a copy of this transform with its [member basis] orthonormalized. An orthonormal basis is both [i]orthogonal[/i] (the axes are perpendicular to each other) and [i]normalized[/i] (the axes have a length of [code]1.0[/code]), which also means it can only represent a rotation. See also [method Basis.orthonormalized]. @@ -118,7 +118,7 @@ Returns a copy of this transform rotated around the given [param axis] by the given [param angle] (in radians). - The [param axis] must be a normalized vector. + The [param axis] must be a normalized vector (see [method Vector3.normalized]). If [param angle] is positive, the basis is rotated counter-clockwise around the axis. This method is an optimized version of multiplying the given transform [code]X[/code] with a corresponding rotation transform [code]R[/code] from the left, i.e., [code]R * X[/code]. This can be seen as transforming with respect to the global/parent frame. @@ -181,8 +181,25 @@ - A transform with no translation, no rotation, and its scale being [code]1[/code]. Its [member basis] is equal to [constant Basis.IDENTITY]. - When multiplied by another [Variant] such as [AABB] or another [Transform3D], no transformation occurs. + The identity [Transform3D]. This is a transform with no translation, no rotation, and a scale of [constant Vector3.ONE]. Its [member basis] is equal to [constant Basis.IDENTITY]. This also means that: + - Its [member Basis.x] points right ([constant Vector3.RIGHT]); + - Its [member Basis.y] points up ([constant Vector3.UP]); + - Its [member Basis.z] points back ([constant Vector3.BACK]). + [codeblock] + var transform = Transform3D.IDENTITY + var basis = transform.basis + print("| X | Y | Z | Origin") + print("| %.f | %.f | %.f | %.f" % [basis.x.x, basis.y.x, basis.z.x, transform.origin.x]) + print("| %.f | %.f | %.f | %.f" % [basis.x.y, basis.y.y, basis.z.y, transform.origin.y]) + print("| %.f | %.f | %.f | %.f" % [basis.x.z, basis.y.z, basis.z.z, transform.origin.z]) + # Prints: + # | X | Y | Z | Origin + # | 1 | 0 | 0 | 0 + # | 0 | 1 | 0 | 0 + # | 0 | 0 | 1 | 0 + [/codeblock] + If a [Vector3], an [AABB], a [Plane], a [PackedVector3Array], or another [Transform3D] is transformed (multiplied) by this constant, no transformation occurs. + [b]Note:[/b] In GDScript, this constant is equivalent to creating a [constructor Transform3D] without any arguments. It can be used to make your code clearer, and for consistency with C#. [Transform3D] with mirroring applied perpendicular to the YZ plane. Its [member basis] is equal to [constant Basis.FLIP_X]. diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 9510d237da9..9fad1ca9a85 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -341,6 +341,9 @@ If [code]true[/code], allows navigating the [Tree] with letter keys through incremental search. + + If [code]true[/code], tree items with no tooltip assigned display their text as their tooltip. See also [method TreeItem.get_tooltip_text] and [method TreeItem.get_button_tooltip_text]. + If [code]true[/code], column titles are visible. diff --git a/doc/classes/TubeTrailMesh.xml b/doc/classes/TubeTrailMesh.xml index bf16b3d16af..4408280f42a 100644 --- a/doc/classes/TubeTrailMesh.xml +++ b/doc/classes/TubeTrailMesh.xml @@ -19,7 +19,7 @@ If [code]true[/code], generates a cap at the top of the tube. This can be set to [code]false[/code] to speed up generation and rendering when the cap is never seen by the camera. - Determines the radius of the tube along its length. The radius of a particular section ring is obtained by multiplying the baseline [member radius] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted. + Determines the radius of the tube along its length. The radius of a particular section ring is obtained by multiplying the baseline [member radius] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted. Should be a unit [Curve]. The number of sides on the tube. For example, a value of [code]5[/code] means the tube will be pentagonal. Higher values result in a more detailed tube at the cost of performance. diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 5320fe3567b..c760e5a6e4d 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -89,7 +89,7 @@ [/codeblocks] Some [Tweener]s use transitions and eases. The first accepts a [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best. [url=https://raw.githubusercontent.com/Redot-Engine/redot-docs/master/img/tween_cheatsheet.webp]Tween easing and transition types cheatsheet[/url] - [b]Note:[/b] Tweens are not designed to be re-used and trying to do so results in an undefined behavior. Create a new Tween for each animation and every time you replay an animation from start. Keep in mind that Tweens start immediately, so only create a Tween when you want to start animating. + [b]Note:[/b] Tweens are not designed to be reused and trying to do so results in an undefined behavior. Create a new Tween for each animation and every time you replay an animation from start. Keep in mind that Tweens start immediately, so only create a Tween when you want to start animating. [b]Note:[/b] The tween is processed after all of the nodes in the current frame, i.e. node's [method Node._process] method would be called before the tween (or [method Node._physics_process] depending on the value passed to [method set_process_mode]). diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml index 123ae399b33..59a12a1dafb 100644 --- a/doc/classes/UDPServer.xml +++ b/doc/classes/UDPServer.xml @@ -13,7 +13,7 @@ class_name ServerNode extends Node - var server := UDPServer.new() + var server = UDPServer.new() var peers = [] func _ready(): @@ -22,7 +22,7 @@ func _process(delta): server.poll() # Important! if server.is_connection_available(): - var peer: PacketPeerUDP = server.take_connection() + var peer = server.take_connection() var packet = peer.get_packet() print("Accepted peer: %s:%s" % [peer.get_packet_ip(), peer.get_packet_port()]) print("Received data: %s" % [packet.get_string_from_utf8()]) @@ -77,7 +77,7 @@ class_name ClientNode extends Node - var udp := PacketPeerUDP.new() + var udp = PacketPeerUDP.new() var connected = false func _ready(): diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 3e772c4e884..73d22873cab 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -405,7 +405,7 @@ If [code]true[/code], 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be an [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be an [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. - [b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons. + [b]Note:[/b] This setting will have no effect when using the Compatibility renderer, which always renders in low dynamic range for performance reasons. If [code]true[/code], [OccluderInstance3D] nodes will be usable for occlusion culling in 3D for this viewport. For the root viewport, [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling] must be set to [code]true[/code] instead. diff --git a/doc/classes/VisualShaderNodeDerivativeFunc.xml b/doc/classes/VisualShaderNodeDerivativeFunc.xml index 6dc38a00ac8..c9bed5ead9b 100644 --- a/doc/classes/VisualShaderNodeDerivativeFunc.xml +++ b/doc/classes/VisualShaderNodeDerivativeFunc.xml @@ -16,7 +16,7 @@ A type of operands and returned value. See [enum OpType] for options. - Sets the level of precision to use for the derivative function. See [enum Precision] for options. When using the GL Compatibility renderer, this setting has no effect. + Sets the level of precision to use for the derivative function. See [enum Precision] for options. When using the Compatibility renderer, this setting has no effect. diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 6e833ef25a5..6d3294cc275 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -727,7 +727,7 @@ Emitted when files are dragged from the OS file manager and dropped in the game window. The argument is a list of file paths. [codeblock] func _ready(): - get_viewport().files_dropped.connect(on_files_dropped) + get_window().files_dropped.connect(on_files_dropped) func on_files_dropped(files): print(files) @@ -805,6 +805,7 @@ Full screen mode with full multi-window support. Full screen window covers the entire display area of a screen and has no decorations. The display's video mode is not changed. + [b]On Android:[/b] This enables immersive mode. [b]On Windows:[/b] Multi-window full-screen mode has a 1px border of the [member ProjectSettings.rendering/environment/defaults/default_clear_color] color. [b]On macOS:[/b] A new desktop is used to display the running project. [b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode. @@ -812,6 +813,7 @@ A single window full screen mode. This mode has less overhead, but only one window can be open on a given screen at a time (opening a child window or application switching will trigger a full screen transition). Full screen window covers the entire display area of a screen and has no border or decorations. The display's video mode is not changed. + [b]On Android:[/b] This enables immersive mode. [b]On Windows:[/b] Depending on video driver, full screen transition might cause screens to go black for a moment. [b]On macOS:[/b] A new desktop is used to display the running project. Exclusive full screen mode prevents Dock and Menu from showing up when the mouse pointer is hovering the edge of the screen. [b]On Linux (X11):[/b] Exclusive full screen mode bypasses compositor. diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 38b7b2387be..280f4948bc8 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -911,7 +911,7 @@ def get_git_branch() -> str: def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: str) -> None: class_name = class_def.name with open( - os.devnull if dry_run else os.path.join(output_dir, f"class_{class_name.lower()}.rst"), + os.devnull if dry_run else os.path.join(output_dir, f"class_{sanitize_class_name(class_name, True)}.rst"), "w", encoding="utf-8", newline="\n", @@ -937,7 +937,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write(f".. XML source: {source_github_url}.\n\n") # Document reference id and header. - f.write(f".. _class_{class_name}:\n\n") + f.write(f".. _class_{sanitize_class_name(class_name)}:\n\n") f.write(make_heading(class_name, "=", False)) f.write(make_deprecated_experimental(class_def, state)) @@ -1041,13 +1041,11 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: type_rst = property_def.type_name.to_rst(state) default = property_def.default_value if default is not None and property_def.overrides: - ref = ( - f":ref:`{property_def.overrides}`" - ) + ref = f":ref:`{property_def.overrides}`" # Not using translate() for now as it breaks table formatting. ml.append((type_rst, property_def.name, f"{default} (overrides {ref})")) else: - ref = f":ref:`{property_def.name}`" + ref = f":ref:`{property_def.name}`" ml.append((type_rst, ref, default)) format_table(f, ml, True) @@ -1093,7 +1091,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: ml = [] for theme_item_def in class_def.theme_items.values(): - ref = f":ref:`{theme_item_def.name}`" + ref = f":ref:`{theme_item_def.name}`" ml.append((theme_item_def.type_name.to_rst(state), ref, theme_item_def.default_value)) format_table(f, ml, True) @@ -1114,7 +1112,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create signal signature and anchor point. - signal_anchor = f"class_{class_name}_signal_{signal.name}" + signal_anchor = f"class_{sanitize_class_name(class_name)}_signal_{signal.name}" f.write(f".. _{signal_anchor}:\n\n") self_link = f":ref:`🔗<{signal_anchor}>`" f.write(".. rst-class:: classref-signal\n\n") @@ -1153,7 +1151,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create enumeration signature and anchor point. - enum_anchor = f"enum_{class_name}_{e.name}" + enum_anchor = f"enum_{sanitize_class_name(class_name)}_{e.name}" f.write(f".. _{enum_anchor}:\n\n") self_link = f":ref:`🔗<{enum_anchor}>`" f.write(".. rst-class:: classref-enumeration\n\n") @@ -1166,7 +1164,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: for value in e.values.values(): # Also create signature and anchor point for each enum constant. - f.write(f".. _class_{class_name}_constant_{value.name}:\n\n") + f.write(f".. _class_{sanitize_class_name(class_name)}_constant_{value.name}:\n\n") f.write(".. rst-class:: classref-enumeration-constant\n\n") f.write(f"{e.type_name.to_rst(state)} **{value.name}** = ``{value.value}``\n\n") @@ -1199,7 +1197,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: for constant in class_def.constants.values(): # Create constant signature and anchor point. - constant_anchor = f"class_{class_name}_constant_{constant.name}" + constant_anchor = f"class_{sanitize_class_name(class_name)}_constant_{constant.name}" f.write(f".. _{constant_anchor}:\n\n") self_link = f":ref:`🔗<{constant_anchor}>`" f.write(".. rst-class:: classref-constant\n\n") @@ -1239,7 +1237,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: self_link = "" if i == 0: - annotation_anchor = f"class_{class_name}_annotation_{m.name}" + annotation_anchor = f"class_{sanitize_class_name(class_name)}_annotation_{m.name}" f.write(f".. _{annotation_anchor}:\n\n") self_link = f" :ref:`🔗<{annotation_anchor}>`" @@ -1280,7 +1278,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create property signature and anchor point. - property_anchor = f"class_{class_name}_property_{property_def.name}" + property_anchor = f"class_{sanitize_class_name(class_name)}_property_{property_def.name}" f.write(f".. _{property_anchor}:\n\n") self_link = f":ref:`🔗<{property_anchor}>`" f.write(".. rst-class:: classref-property\n\n") @@ -1348,7 +1346,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: self_link = "" if i == 0: - constructor_anchor = f"class_{class_name}_constructor_{m.name}" + constructor_anchor = f"class_{sanitize_class_name(class_name)}_constructor_{m.name}" f.write(f".. _{constructor_anchor}:\n\n") self_link = f" :ref:`🔗<{constructor_anchor}>`" @@ -1394,7 +1392,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: method_qualifier = "" if m.name.startswith("_"): method_qualifier = "private_" - method_anchor = f"class_{class_name}_{method_qualifier}method_{m.name}" + method_anchor = f"class_{sanitize_class_name(class_name)}_{method_qualifier}method_{m.name}" f.write(f".. _{method_anchor}:\n\n") self_link = f" :ref:`🔗<{method_anchor}>`" @@ -1435,7 +1433,9 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create operator signature and anchor point. - operator_anchor = f"class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" + operator_anchor = ( + f"class_{sanitize_class_name(class_name)}_operator_{sanitize_operator_name(m.name, state)}" + ) for parameter in m.parameters: operator_anchor += f"_{parameter.type_name.type_name}" f.write(f".. _{operator_anchor}:\n\n") @@ -1477,7 +1477,9 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create theme property signature and anchor point. - theme_item_anchor = f"class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}" + theme_item_anchor = ( + f"class_{sanitize_class_name(class_name)}_theme_{theme_item_def.data_name}_{theme_item_def.name}" + ) f.write(f".. _{theme_item_anchor}:\n\n") self_link = f":ref:`🔗<{theme_item_anchor}>`" f.write(".. rst-class:: classref-themeproperty\n\n") @@ -1515,7 +1517,7 @@ def make_type(klass: str, state: State) -> str: def resolve_type(link_type: str) -> str: if link_type in state.classes: - return f":ref:`{link_type}`" + return f":ref:`{link_type}`" else: print_error(f'{state.current_class}.xml: Unresolved type "{link_type}".', state) return f"``{link_type}``" @@ -1533,7 +1535,7 @@ def resolve_type(link_type: str) -> str: def make_enum(t: str, is_bitfield: bool, state: State) -> str: - p = t.find(".") + p = t.rfind(".") if p >= 0: c = t[0:p] e = t[p + 1 :] @@ -1551,9 +1553,9 @@ def make_enum(t: str, is_bitfield: bool, state: State) -> str: if is_bitfield: if not state.classes[c].enums[e].is_bitfield: print_error(f'{state.current_class}.xml: Enum "{t}" is not bitfield.', state) - return f"|bitfield|\\[:ref:`{e}`\\]" + return f"|bitfield|\\[:ref:`{e}`\\]" else: - return f":ref:`{e}`" + return f":ref:`{e}`" print_error(f'{state.current_class}.xml: Unresolved enum "{t}".', state) @@ -1576,7 +1578,7 @@ def make_method_signature( if isinstance(definition, MethodDef) and ref_type != "": if ref_type == "operator": op_name = definition.name.replace("<", "\\<") # So operator "<" gets correctly displayed. - out += f":ref:`{op_name}`" + out += f":ref:`{definition.name}`" else: - out += f":ref:`{definition.name}`" + out += f":ref:`{definition.name}`" else: out += f"**{definition.name}**" @@ -1774,13 +1776,15 @@ def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_ f.write("\n") if group_name in CLASS_GROUPS_BASE: - f.write(f" class_{CLASS_GROUPS_BASE[group_name].lower()}\n") + f.write(f" class_{sanitize_class_name(CLASS_GROUPS_BASE[group_name], True)}\n") for class_name in grouped_classes[group_name]: - if group_name in CLASS_GROUPS_BASE and CLASS_GROUPS_BASE[group_name].lower() == class_name.lower(): + if group_name in CLASS_GROUPS_BASE and sanitize_class_name( + CLASS_GROUPS_BASE[group_name], True + ) == sanitize_class_name(class_name, True): continue - f.write(f" class_{class_name.lower()}\n") + f.write(f" class_{sanitize_class_name(class_name, True)}\n") f.write("\n") @@ -2261,7 +2265,7 @@ def format_text_block( repl_text = target_name if target_class_name != state.current_class: repl_text = f"{target_class_name}.{target_name}" - tag_text = f":ref:`{repl_text}`" + tag_text = f":ref:`{repl_text}`" escape_pre = True escape_post = True @@ -2574,6 +2578,13 @@ def format_table(f: TextIO, data: List[Tuple[Optional[str], ...]], remove_empty_ f.write("\n") +def sanitize_class_name(dirty_name: str, is_file_name=False) -> str: + if is_file_name: + return dirty_name.lower().replace('"', "").replace("/", "--") + else: + return dirty_name.replace('"', "").replace("/", "_").replace(".", "_") + + def sanitize_operator_name(dirty_name: str, state: State) -> str: clear_name = dirty_name.replace("operator ", "") diff --git a/drivers/metal/rendering_context_driver_metal.mm b/drivers/metal/rendering_context_driver_metal.mm index a3022e0bdb2..2bd24aa814c 100644 --- a/drivers/metal/rendering_context_driver_metal.mm +++ b/drivers/metal/rendering_context_driver_metal.mm @@ -174,7 +174,11 @@ void present(MDCommandBuffer *p_cmd_buffer) override final { count--; front = (front + 1) % frame_buffers.size(); - [p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration]; + if (vsync_mode != DisplayServer::VSYNC_DISABLED) { + [p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration]; + } else { + [p_cmd_buffer->get_command_buffer() presentDrawable:drawable]; + } } }; diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index 7503558ee2d..34b9f805b08 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -360,11 +360,25 @@ _FORCE_INLINE_ MTLSize mipmapLevelSizeFromSize(MTLSize p_size, NSUInteger p_leve } RDD::TextureID RenderingDeviceDriverMetal::texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) { - id obj = (__bridge id)(void *)(uintptr_t)p_native_texture; - - // We only need to create a RDD::TextureID for an existing, natively-provided texture. - - return rid::make(obj); + id res = (__bridge id)(void *)(uintptr_t)p_native_texture; + + // If the requested format is different, we need to create a view. + MTLPixelFormat format = pixel_formats->getMTLPixelFormat(p_format); + if (res.pixelFormat != format) { + MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake( + MTLTextureSwizzleRed, + MTLTextureSwizzleGreen, + MTLTextureSwizzleBlue, + MTLTextureSwizzleAlpha); + res = [res newTextureViewWithPixelFormat:format + textureType:res.textureType + levels:NSMakeRange(0, res.mipmapLevelCount) + slices:NSMakeRange(0, p_array_layers) + swizzle:swizzle]; + ERR_FAIL_NULL_V_MSG(res, TextureID(), "Unable to create texture view."); + } + + return rid::make(res); } RDD::TextureID RenderingDeviceDriverMetal::texture_create_shared(TextureID p_original_texture, const TextureView &p_view) { diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 79428900b65..79e510500c4 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -48,10 +48,18 @@ #include #endif -void FileAccessUnix::check_errors() const { +void FileAccessUnix::check_errors(bool p_write) const { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - if (feof(f)) { + last_error = OK; + if (ferror(f)) { + if (p_write) { + last_error = ERR_FILE_CANT_WRITE; + } else { + last_error = ERR_FILE_CANT_READ; + } + } + if (!p_write && feof(f)) { last_error = ERR_FILE_EOF; } } @@ -219,7 +227,6 @@ String FileAccessUnix::get_real_path() const { void FileAccessUnix::seek(uint64_t p_position) { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - last_error = OK; if (fseeko(f, p_position, SEEK_SET)) { check_errors(); } @@ -258,7 +265,7 @@ uint64_t FileAccessUnix::get_length() const { } bool FileAccessUnix::eof_reached() const { - return last_error == ERR_FILE_EOF; + return feof(f); } uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const { @@ -297,10 +304,12 @@ void FileAccessUnix::flush() { fflush(f); } -void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - ERR_FAIL_COND(!p_src && p_length > 0); - ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length); +bool FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_NULL_V_MSG(f, false, "File must be opened before use."); + ERR_FAIL_COND_V(!p_src && p_length > 0, false); + bool res = fwrite(p_src, 1, p_length, f) == p_length; + check_errors(true); + return res; } bool FileAccessUnix::file_exists(const String &p_path) { diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 55607298c74..f50a2ab58e1 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -45,7 +45,7 @@ typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags); class FileAccessUnix : public FileAccess { FILE *f = nullptr; int flags = 0; - void check_errors() const; + void check_errors(bool p_write = false) const; mutable Error last_error = OK; String save_path; String path; @@ -79,7 +79,7 @@ class FileAccessUnix : public FileAccess { virtual Error resize(int64_t p_length) override; virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_path) override; ///< return true if a file exists diff --git a/drivers/unix/file_access_unix_pipe.cpp b/drivers/unix/file_access_unix_pipe.cpp index e12a53cb775..0308a1537ae 100644 --- a/drivers/unix/file_access_unix_pipe.cpp +++ b/drivers/unix/file_access_unix_pipe.cpp @@ -152,14 +152,16 @@ Error FileAccessUnixPipe::get_error() const { return last_error; } -void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use."); - ERR_FAIL_COND(!p_src && p_length > 0); +bool FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND_V_MSG(fd[1] < 0, false, "Pipe must be opened before use."); + ERR_FAIL_COND_V(!p_src && p_length > 0, false); if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) { last_error = ERR_FILE_CANT_WRITE; + return false; } else { last_error = OK; + return true; } } diff --git a/drivers/unix/file_access_unix_pipe.h b/drivers/unix/file_access_unix_pipe.h index cd14af97f33..3ccac0c01cf 100644 --- a/drivers/unix/file_access_unix_pipe.h +++ b/drivers/unix/file_access_unix_pipe.h @@ -73,7 +73,7 @@ class FileAccessUnixPipe : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override {} - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_path) override { return false; } diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 75bccf048c5..78e33c17919 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -310,6 +310,10 @@ String OS_Unix::get_version() const { return ""; } +String OS_Unix::get_temp_path() const { + return "/tmp"; +} + double OS_Unix::get_unix_time() const { struct timeval tv_now; gettimeofday(&tv_now, nullptr); diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 4a6145fc6e8..d52ac21fa96 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -78,6 +78,8 @@ class OS_Unix : public OS { virtual String get_distribution_name() const override; virtual String get_version() const override; + virtual String get_temp_path() const override; + virtual DateTime get_datetime(bool p_utc) const override; virtual TimeZoneInfo get_time_zone_info() const override; diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 5e3b0644fda..036b0111320 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -94,7 +94,7 @@ __CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7 #endif // __MINGW32__ || __MINGW64__ -#ifndef PKEY_Device_FriendlyName +#ifndef PKEY_Device_FriendlyNameGodot #undef DEFINE_PROPERTYKEY /* clang-format off */ @@ -102,7 +102,7 @@ __CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7 const PROPERTYKEY id = { { a, b, c, { d, e, f, g, h, i, j, k, } }, l }; /* clang-format on */ -DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyNameGodot, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); #endif const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); @@ -236,7 +236,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i PROPVARIANT propvar; PropVariantInit(&propvar); - hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); + hr = props->GetValue(PKEY_Device_FriendlyNameGodot, &propvar); ERR_BREAK(hr != S_OK); if (p_device->device_name == String(propvar.pwszVal)) { @@ -599,7 +599,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { PROPVARIANT propvar; PropVariantInit(&propvar); - hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); + hr = props->GetValue(PKEY_Device_FriendlyNameGodot, &propvar); ERR_BREAK(hr != S_OK); list.push_back(String(propvar.pwszVal)); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 5e858cc0fad..6d6e67edc98 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -54,10 +54,18 @@ #define S_ISREG(m) ((m) & _S_IFREG) #endif -void FileAccessWindows::check_errors() const { +void FileAccessWindows::check_errors(bool p_write) const { ERR_FAIL_NULL(f); - if (feof(f)) { + last_error = OK; + if (ferror(f)) { + if (p_write) { + last_error = ERR_FILE_CANT_WRITE; + } else { + last_error = ERR_FILE_CANT_READ; + } + } + if (!p_write && feof(f)) { last_error = ERR_FILE_EOF; } } @@ -286,7 +294,6 @@ bool FileAccessWindows::is_open() const { void FileAccessWindows::seek(uint64_t p_position) { ERR_FAIL_NULL(f); - last_error = OK; if (_fseeki64(f, p_position, SEEK_SET)) { check_errors(); } @@ -322,8 +329,7 @@ uint64_t FileAccessWindows::get_length() const { } bool FileAccessWindows::eof_reached() const { - check_errors(); - return last_error == ERR_FILE_EOF; + return feof(f); } uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const { @@ -374,9 +380,9 @@ void FileAccessWindows::flush() { } } -void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_NULL(f); - ERR_FAIL_COND(!p_src && p_length > 0); +bool FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_NULL_V(f, false); + ERR_FAIL_COND_V(!p_src && p_length > 0, false); if (flags == READ_WRITE || flags == WRITE_READ) { if (prev_op == READ) { @@ -387,7 +393,9 @@ void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { prev_op = WRITE; } - ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != (size_t)p_length); + bool res = fwrite(p_src, 1, p_length, f) == (size_t)p_length; + check_errors(true); + return res; } bool FileAccessWindows::file_exists(const String &p_name) { @@ -527,6 +535,9 @@ void FileAccessWindows::initialize() { invalid_files.insert(reserved_files[reserved_file_index]); reserved_file_index++; } + + _setmaxstdio(8192); + print_verbose(vformat("Maximum number of file handles: %d", _getmaxstdio())); } void FileAccessWindows::finalize() { diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 18e6978001e..3568b1233bd 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -43,7 +43,7 @@ class FileAccessWindows : public FileAccess { FILE *f = nullptr; int flags = 0; - void check_errors() const; + void check_errors(bool p_write = false) const; mutable int prev_op = 0; mutable Error last_error = OK; String path; @@ -77,7 +77,7 @@ class FileAccessWindows : public FileAccess { virtual Error resize(int64_t p_length) override; virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/drivers/windows/file_access_windows_pipe.cpp b/drivers/windows/file_access_windows_pipe.cpp index b931757398a..3f1a2ca141e 100644 --- a/drivers/windows/file_access_windows_pipe.cpp +++ b/drivers/windows/file_access_windows_pipe.cpp @@ -121,16 +121,18 @@ Error FileAccessWindowsPipe::get_error() const { return last_error; } -void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_COND_MSG(fd[1] == nullptr, "Pipe must be opened before use."); - ERR_FAIL_COND(!p_src && p_length > 0); +bool FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND_V_MSG(fd[1] == nullptr, false, "Pipe must be opened before use."); + ERR_FAIL_COND_V(!p_src && p_length > 0, false); DWORD read = -1; bool ok = WriteFile(fd[1], p_src, p_length, &read, nullptr); if (!ok || read != p_length) { last_error = ERR_FILE_CANT_WRITE; + return false; } else { last_error = OK; + return true; } } diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h index 42eec59df37..77b0fa30954 100644 --- a/drivers/windows/file_access_windows_pipe.h +++ b/drivers/windows/file_access_windows_pipe.h @@ -72,7 +72,7 @@ class FileAccessWindowsPipe : public FileAccess { virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override {} - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override { return false; } diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 89592380288..a5c01aeb970 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -573,7 +573,7 @@ ActionMapEditor::ActionMapEditor() { add_edit->set_placeholder(TTR("Add New Action")); add_edit->set_clear_button_enabled(true); add_edit->connect(SceneStringName(text_changed), callable_mp(this, &ActionMapEditor::_add_edit_text_changed)); - add_edit->connect("text_submitted", callable_mp(this, &ActionMapEditor::_add_action)); + add_edit->connect(SceneStringName(text_submitted), callable_mp(this, &ActionMapEditor::_add_action)); add_hbox->add_child(add_edit); add_button = memnew(Button); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 5210092a3fc..e801ece4db6 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1300,12 +1300,12 @@ void AnimationTimelineEdit::_zoom_changed(double) { double timeline_right = timeline_left + timeline_width_seconds; double timeline_center = timeline_left + timeline_width_seconds / 2.0; - if (zoom_callback_occured) { // Zooming with scroll wheel will focus on the position of the mouse. + if (zoom_callback_occurred) { // Zooming with scroll wheel will focus on the position of the mouse. double zoom_scroll_origin_norm = (zoom_scroll_origin.x - get_name_limit()) / timeline_width_pixels; zoom_scroll_origin_norm = MAX(zoom_scroll_origin_norm, 0); zoom_pivot = timeline_left + timeline_width_seconds * zoom_scroll_origin_norm; zoom_pivot_delta = updated_timeline_width_seconds * zoom_scroll_origin_norm; - zoom_callback_occured = false; + zoom_callback_occurred = false; } else { // Zooming with slider will depend on the current play position. // If the play position is not in range, or exactly in the center, zoom in on the center. if (get_play_position() < timeline_left || get_play_position() > timeline_left + timeline_width_seconds || get_play_position() == timeline_center) { @@ -1954,7 +1954,7 @@ void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec, Ref void AnimationTimelineEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref p_event) { double current_zoom_value = get_zoom()->get_value(); zoom_scroll_origin = p_origin; - zoom_callback_occured = true; + zoom_callback_occurred = true; get_zoom()->set_value(MAX(0.01, current_zoom_value - (1.0 - p_zoom_factor))); } @@ -3126,7 +3126,7 @@ void AnimationTrackEdit::gui_input(const Ref &p_event) { path = memnew(LineEdit); path_popup->add_child(path); path->set_anchors_and_offsets_preset(PRESET_FULL_RECT); - path->connect("text_submitted", callable_mp(this, &AnimationTrackEdit::_path_submitted)); + path->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationTrackEdit::_path_submitted)); } path->set_text(animation->track_get_path(track)); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index e0a3c7197a5..50c67aaa093 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -234,7 +234,7 @@ class AnimationTimelineEdit : public Range { double hscroll_on_zoom_buffer = -1.0; Vector2 zoom_scroll_origin; - bool zoom_callback_occured = false; + bool zoom_callback_occurred = false; virtual void gui_input(const Ref &p_event) override; void _track_added(int p_track); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index eb28a0c0761..4cbe3710fc9 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -760,7 +760,7 @@ FindReplaceBar::FindReplaceBar() { search_text->set_tooltip_text(TTR("Find")); search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); search_text->connect(SceneStringName(text_changed), callable_mp(this, &FindReplaceBar::_search_text_changed)); - search_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_search_text_submitted)); + search_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindReplaceBar::_search_text_submitted)); search_text->connect(SceneStringName(focus_exited), callable_mp(this, &FindReplaceBar::_focus_lost)); matches_label = memnew(Label); @@ -799,7 +799,7 @@ FindReplaceBar::FindReplaceBar() { replace_text->set_placeholder(TTR("Replace")); replace_text->set_tooltip_text(TTR("Replace")); replace_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - replace_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_replace_text_submitted)); + replace_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindReplaceBar::_replace_text_submitted)); replace_text->connect(SceneStringName(focus_exited), callable_mp(this, &FindReplaceBar::_focus_lost)); replace = memnew(Button); diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index 12c6481e475..6cf9de05799 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -1249,7 +1249,7 @@ DebugAdapterProtocol::DebugAdapterProtocol() { debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled)); debugger_node->get_default_debugger()->connect("stopped", callable_mp(this, &DebugAdapterProtocol::on_debug_stopped)); - debugger_node->get_default_debugger()->connect("output", callable_mp(this, &DebugAdapterProtocol::on_debug_output)); + debugger_node->get_default_debugger()->connect(SceneStringName(output), callable_mp(this, &DebugAdapterProtocol::on_debug_output)); debugger_node->get_default_debugger()->connect("breaked", callable_mp(this, &DebugAdapterProtocol::on_debug_breaked)); debugger_node->get_default_debugger()->connect("stack_dump", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_dump)); debugger_node->get_default_debugger()->connect("stack_frame_vars", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_vars)); diff --git a/editor/debugger/editor_expression_evaluator.cpp b/editor/debugger/editor_expression_evaluator.cpp index 910ac7e1348..3016f81dab5 100644 --- a/editor/debugger/editor_expression_evaluator.cpp +++ b/editor/debugger/editor_expression_evaluator.cpp @@ -112,7 +112,7 @@ EditorExpressionEvaluator::EditorExpressionEvaluator() { expression_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); expression_input->set_placeholder(TTR("Expression to evaluate")); expression_input->set_clear_button_enabled(true); - expression_input->connect("text_submitted", callable_mp(this, &EditorExpressionEvaluator::_evaluate).unbind(1)); + expression_input->connect(SceneStringName(text_submitted), callable_mp(this, &EditorExpressionEvaluator::_evaluate).unbind(1)); expression_input->connect(SceneStringName(text_changed), callable_mp(this, &EditorExpressionEvaluator::_on_expression_input_changed)); hb->add_child(expression_input); diff --git a/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp index d74850128c6..06fe053ab19 100644 --- a/editor/debugger/editor_performance_profiler.cpp +++ b/editor/debugger/editor_performance_profiler.cpp @@ -398,10 +398,14 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() { set_split_offset(340 * EDSCALE); monitor_tree = memnew(Tree); + monitor_tree->set_custom_minimum_size(Size2(300, 0) * EDSCALE); monitor_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); monitor_tree->set_columns(2); monitor_tree->set_column_title(0, TTR("Monitor")); + monitor_tree->set_column_expand(0, true); monitor_tree->set_column_title(1, TTR("Value")); + monitor_tree->set_column_custom_minimum_width(1, 100 * EDSCALE); + monitor_tree->set_column_expand(1, false); monitor_tree->set_column_titles_visible(true); monitor_tree->connect("item_edited", callable_mp(this, &EditorPerformanceProfiler::_monitor_select)); monitor_tree->create_item(); @@ -410,6 +414,7 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() { add_child(monitor_tree); monitor_draw = memnew(Control); + monitor_draw->set_custom_minimum_size(Size2(300, 0) * EDSCALE); monitor_draw->set_clip_contents(true); monitor_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPerformanceProfiler::_monitor_draw)); monitor_draw->connect(SceneStringName(gui_input), callable_mp(this, &EditorPerformanceProfiler::_marker_input)); diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index e61c3d8e048..dc00a061266 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -154,6 +154,11 @@ Color EditorProfiler::_get_color_from_signature(const StringName &p_signature) c return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07); } +int EditorProfiler::_get_zoom_left_border() const { + const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom); + return CLAMP(zoom_center - max_profiles_shown / 2, 0, frame_metrics.size() - max_profiles_shown); +} + void EditorProfiler::_item_edited() { if (updating_frame) { return; @@ -239,12 +244,17 @@ void EditorProfiler::_update_plot() { HashMap prev_plots; - for (int i = 0; i < total_metrics * w / frame_metrics.size() - 1; i++) { + const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom); + const int left_border = _get_zoom_left_border(); + const int profiles_drawn = CLAMP(total_metrics - left_border, 0, max_profiles_shown); + const int pixel_cols = (profiles_drawn * w) / max_profiles_shown - 1; + + for (int i = 0; i < pixel_cols; i++) { for (int j = 0; j < h * 4; j++) { column[j] = 0; } - int current = i * frame_metrics.size() / w; + int current = (i * max_profiles_shown / w) + left_border; for (const StringName &E : plot_sigs) { const Metric &m = _get_frame_metric(current); @@ -451,10 +461,12 @@ void EditorProfiler::_graph_tex_draw() { } if (seeking) { int frame = cursor_metric_edit->get_value() - _get_frame_metric(0).frame_number; - int cur_x = (2 * frame + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1; + frame = frame - _get_zoom_left_border() + 1; + int cur_x = (frame * graph->get_size().width * Math::exp(graph_zoom)) / frame_metrics.size(); + cur_x = CLAMP(cur_x, 0, graph->get_size().width); graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_color); } - if (hover_metric > -1 && hover_metric < total_metrics) { + if (hover_metric > -1) { int cur_x = (2 * hover_metric + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1; graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_hover_color); } @@ -482,22 +494,17 @@ void EditorProfiler::_graph_tex_input(const Ref &p_ev) { Ref me = p_ev; Ref mb = p_ev; Ref mm = p_ev; + MouseButton button_idx = mb.is_valid() ? mb->get_button_index() : MouseButton(); if ( - (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) || + (mb.is_valid() && button_idx == MouseButton::LEFT && mb->is_pressed()) || (mm.is_valid())) { int x = me->get_position().x - 1; - x = x * frame_metrics.size() / graph->get_size().width; - - hover_metric = x; + hover_metric = x * frame_metrics.size() / graph->get_size().width; - if (x < 0) { - x = 0; - } - - if (x >= frame_metrics.size()) { - x = frame_metrics.size() - 1; - } + x = x * frame_metrics.size() / graph->get_size().width; + x = x / Math::exp(graph_zoom) + _get_zoom_left_border(); + x = CLAMP(x, 0, frame_metrics.size() - 1); if (mb.is_valid() || (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { updating_frame = true; @@ -520,9 +527,34 @@ void EditorProfiler::_graph_tex_input(const Ref &p_ev) { frame_delay->start(); } } + } + + if (graph_zoom > 0 && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::MIDDLE) || mm->get_button_mask().has_flag(MouseButtonMask::RIGHT))) { + // Panning. + const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom); + pan_accumulator += (float)mm->get_relative().x * max_profiles_shown / graph->get_size().width; + + if (Math::abs(pan_accumulator) > 1) { + zoom_center = CLAMP(zoom_center - (int)pan_accumulator, max_profiles_shown / 2, frame_metrics.size() - max_profiles_shown / 2); + pan_accumulator -= (int)pan_accumulator; + _update_plot(); + } + } - graph->queue_redraw(); + if (button_idx == MouseButton::WHEEL_DOWN) { + // Zooming. + graph_zoom = MAX(-0.05 + graph_zoom, 0); + _update_plot(); + } else if (button_idx == MouseButton::WHEEL_UP) { + if (graph_zoom == 0) { + zoom_center = me->get_position().x; + zoom_center = zoom_center * frame_metrics.size() / graph->get_size().width; + } + graph_zoom = MIN(0.05 + graph_zoom, 2); + _update_plot(); } + + graph->queue_redraw(); } void EditorProfiler::disable_seeking() { @@ -718,6 +750,7 @@ EditorProfiler::EditorProfiler() { variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited)); graph = memnew(TextureRect); + graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0)); graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); graph->set_mouse_filter(MOUSE_FILTER_STOP); graph->connect(SceneStringName(draw), callable_mp(this, &EditorProfiler::_graph_tex_draw)); diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index cca204d67d6..7490bf68302 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -106,6 +106,11 @@ class EditorProfiler : public VBoxContainer { TextureRect *graph = nullptr; Ref graph_texture; Vector graph_image; + + float graph_zoom = 0.0f; + float pan_accumulator = 0.0f; + int zoom_center = -1; + Tree *variables = nullptr; HSplitContainer *h_split = nullptr; @@ -157,6 +162,7 @@ class EditorProfiler : public VBoxContainer { void _graph_tex_input(const Ref &p_ev); Color _get_color_from_signature(const StringName &p_signature) const; + int _get_zoom_left_border() const; void _cursor_metric_changed(double); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 2205a66ef3a..7c4484a9cf0 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -39,6 +39,12 @@ #include "editor/themes/editor_scale.h" #include "scene/resources/image_texture.h" +void EditorVisualProfiler::set_hardware_info(const String &p_cpu_name, const String &p_gpu_name) { + cpu_name = p_cpu_name; + gpu_name = p_gpu_name; + queue_redraw(); +} + void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) { ++last_metric; if (last_metric >= frame_metrics.size()) { @@ -491,8 +497,8 @@ void EditorVisualProfiler::_graph_tex_draw() { graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75)); } - graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1)); - graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1)); + graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU: " + cpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75)); + graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU: " + gpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75)); } void EditorVisualProfiler::_graph_tex_mouse_exit() { @@ -813,6 +819,7 @@ EditorVisualProfiler::EditorVisualProfiler() { variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected)); graph = memnew(TextureRect); + graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0)); graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); graph->set_mouse_filter(MOUSE_FILTER_STOP); graph->connect(SceneStringName(draw), callable_mp(this, &EditorVisualProfiler::_graph_tex_draw)); diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h index d1127828e5c..78080548702 100644 --- a/editor/debugger/editor_visual_profiler.h +++ b/editor/debugger/editor_visual_profiler.h @@ -100,6 +100,9 @@ class EditorVisualProfiler : public VBoxContainer { float graph_limit = 1000.0f / 60; + String cpu_name; + String gpu_name; + bool seeking = false; Timer *frame_delay = nullptr; @@ -138,6 +141,7 @@ class EditorVisualProfiler : public VBoxContainer { static void _bind_methods(); public: + void set_hardware_info(const String &p_cpu_name, const String &p_gpu_name); void add_frame_metric(const Metric &p_metric); void set_enabled(bool p_enable); void set_profiling(bool p_profiling); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 3d3cd548ad2..70f04e5e5c5 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -317,7 +317,7 @@ void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) { ThreadDebugged &td = threads_debugged[p_thread_id]; _set_reason_text(td.error, MESSAGE_ERROR); emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump); - if (!td.error.is_empty()) { + if (!td.error.is_empty() && EDITOR_GET("debugger/auto_switch_to_stack_trace")) { tabs->set_current_tab(0); } inspector->clear_cache(); // Take a chance to force remote objects update. @@ -506,7 +506,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread } break; } EditorNode::get_log()->add_message(output_strings[i], msg_type); - emit_signal(SNAME("output"), output_strings[i], msg_type); + emit_signal(SceneStringName(output), output_strings[i], msg_type); } } else if (p_msg == "performance:profile_frame") { Vector frame_data; @@ -515,6 +515,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread frame_data.write[i] = p_data[i]; } performance_profiler->add_profile_frame(frame_data); + } else if (p_msg == "visual:hardware_info") { + const String cpu_name = p_data[0]; + const String gpu_name = p_data[1]; + visual_profiler->set_hardware_info(cpu_name, gpu_name); } else if (p_msg == "visual:profile_frame") { ServersDebugger::VisualProfilerFrame frame; frame.deserialize(p_data); @@ -1918,6 +1922,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { threads->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_select_thread)); stack_dump = memnew(Tree); + stack_dump->set_custom_minimum_size(Size2(150, 0) * EDSCALE); stack_dump->set_allow_reselect(true); stack_dump->set_columns(1); stack_dump->set_column_titles_visible(true); @@ -1929,6 +1934,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { stack_vb->add_child(stack_dump); VBoxContainer *inspector_vbox = memnew(VBoxContainer); + inspector_vbox->set_custom_minimum_size(Size2(200, 0) * EDSCALE); inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL); sc->add_child(inspector_vbox); @@ -1954,6 +1960,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { inspector_vbox->add_child(inspector); breakpoints_tree = memnew(Tree); + breakpoints_tree->set_custom_minimum_size(Size2(100, 0) * EDSCALE); breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL); breakpoints_tree->set_column_titles_visible(true); breakpoints_tree->set_column_title(0, TTR("Breakpoints")); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index ec36d04e702..64baf122381 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -817,7 +817,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { set_v_size_flags(SIZE_EXPAND_FILL); track_name = memnew(LineEdit); - track_name->connect("text_submitted", callable_mp(this, &EditorAudioBus::_name_changed)); + track_name->connect(SceneStringName(text_submitted), callable_mp(this, &EditorAudioBus::_name_changed)); track_name->connect(SceneStringName(focus_exited), callable_mp(this, &EditorAudioBus::_name_focus_exit)); vb->add_child(track_name); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index d9151a5577a..df5dd9aea7f 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -923,7 +923,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() { autoload_add_name = memnew(LineEdit); autoload_add_name->set_h_size_flags(SIZE_EXPAND_FILL); - autoload_add_name->connect("text_submitted", callable_mp(this, &EditorAutoloadSettings::_autoload_text_submitted)); + autoload_add_name->connect(SceneStringName(text_submitted), callable_mp(this, &EditorAutoloadSettings::_autoload_text_submitted)); autoload_add_name->connect(SceneStringName(text_changed), callable_mp(this, &EditorAutoloadSettings::_autoload_text_changed)); hbc->add_child(autoload_add_name); @@ -977,7 +977,7 @@ EditorAutoloadSettings::~EditorAutoloadSettings() { void EditorAutoloadSettings::_set_autoload_add_path(const String &p_text) { autoload_add_path->set_text(p_text); - autoload_add_path->emit_signal(SNAME("text_submitted"), p_text); + autoload_add_path->emit_signal(SceneStringName(text_submitted), p_text); } void EditorAutoloadSettings::_browse_autoload_add_path() { diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp index 09516abfe08..158bc28e7b7 100644 --- a/editor/editor_dock_manager.cpp +++ b/editor/editor_dock_manager.cpp @@ -176,6 +176,9 @@ void EditorDockManager::_update_docks_menu() { docks_menu_docks.clear(); int id = 0; for (const KeyValue &dock : all_docks) { + if (!dock.value.enabled) { + continue; + } if (dock.value.shortcut.is_valid()) { docks_menu->add_shortcut(dock.value.shortcut, id); docks_menu->set_item_text(id, dock.value.title); @@ -186,8 +189,10 @@ void EditorDockManager::_update_docks_menu() { docks_menu->set_item_icon(id, icon.is_valid() ? icon : default_icon); if (!dock.value.open) { docks_menu->set_item_icon_modulate(id, closed_icon_color_mod); + docks_menu->set_item_tooltip(id, vformat(TTR("Open the %s dock."), dock.value.title)); + } else { + docks_menu->set_item_tooltip(id, vformat(TTR("Focus on the %s dock."), dock.value.title)); } - docks_menu->set_item_disabled(id, !dock.value.enabled); docks_menu_docks.push_back(dock.key); id++; } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index f1ef341d51e..23d3508a8dd 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -3558,5 +3558,9 @@ EditorFileSystem::EditorFileSystem() { } EditorFileSystem::~EditorFileSystem() { + if (filesystem) { + memdelete(filesystem); + } + filesystem = nullptr; ResourceSaver::set_get_resource_id_for_path(nullptr); } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index cf8012f57e4..0c6bbf6edbd 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -2380,11 +2380,7 @@ void EditorHelp::_help_callback(const String &p_topic) { } if (class_desc->is_finished()) { - // call_deferred() is not enough. - if (class_desc->is_connected(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph))) { - class_desc->disconnect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph)); - } - class_desc->connect(SceneStringName(draw), callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).bind(line), CONNECT_ONE_SHOT | CONNECT_DEFERRED); + class_desc->scroll_to_paragraph(line); } else { scroll_to = line; } @@ -2409,6 +2405,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C const Ref doc_code_font = p_owner_node->get_theme_font(SNAME("doc_source"), EditorStringName(EditorFonts)); const Ref doc_kbd_font = p_owner_node->get_theme_font(SNAME("doc_keyboard"), EditorStringName(EditorFonts)); + const int doc_font_size = p_owner_node->get_theme_font_size(SNAME("doc_size"), EditorStringName(EditorFonts)); const int doc_code_font_size = p_owner_node->get_theme_font_size(SNAME("doc_source_size"), EditorStringName(EditorFonts)); const int doc_kbd_font_size = p_owner_node->get_theme_font_size(SNAME("doc_keyboard_size"), EditorStringName(EditorFonts)); @@ -2521,7 +2518,14 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C tag_stack.pop_front(); pos = brk_end + 1; - if (tag != "/img") { + if (tag == "/img") { + // Nothing to do. + } else if (tag == "/url") { + p_rt->pop(); // meta + p_rt->pop(); // color + p_rt->add_text(" "); + p_rt->add_image(p_owner_node->get_editor_theme_icon(SNAME("ExternalLink")), 0, doc_font_size, link_color); + } else { p_rt->pop(); } } else if (tag.begins_with("method ") || tag.begins_with("constructor ") || tag.begins_with("operator ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("annotation ") || tag.begins_with("theme_item ")) { @@ -2789,19 +2793,20 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C } else if (tag == "rb") { p_rt->add_text("]"); pos = brk_end + 1; - } else if (tag == "url") { - int end = bbcode.find_char('[', brk_end); - if (end == -1) { - end = bbcode.length(); + } else if (tag == "url" || tag.begins_with("url=")) { + String url; + if (tag.begins_with("url=")) { + url = tag.substr(4); + } else { + int end = bbcode.find_char('[', brk_end); + if (end == -1) { + end = bbcode.length(); + } + url = bbcode.substr(brk_end + 1, end - brk_end - 1); } - String url = bbcode.substr(brk_end + 1, end - brk_end - 1); - p_rt->push_meta(url); - pos = brk_end + 1; - tag_stack.push_front(tag); - } else if (tag.begins_with("url=")) { - String url = tag.substr(4); - p_rt->push_meta(url); + p_rt->push_color(link_color); + p_rt->push_meta(url, RichTextLabel::META_UNDERLINE_ON_HOVER, url + "\n\n" + TTR("Click to open in browser.")); pos = brk_end + 1; tag_stack.push_front("url"); @@ -4105,7 +4110,7 @@ FindBar::FindBar() { search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); search_text->set_h_size_flags(SIZE_EXPAND_FILL); search_text->connect(SceneStringName(text_changed), callable_mp(this, &FindBar::_search_text_changed)); - search_text->connect("text_submitted", callable_mp(this, &FindBar::_search_text_submitted)); + search_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindBar::_search_text_submitted)); matches_label = memnew(Label); add_child(matches_label); diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index d702e50edce..e0aaf2d471e 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -959,7 +959,7 @@ void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector(&p_methods[i]); + method.doc = &p_methods[i]; r_match_methods->push_back(method); } } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index caf6abc822d..aff24ab17f0 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2536,7 +2536,7 @@ EditorInspectorArray::EditorInspectorArray(bool p_read_only) { new_size_spin_box = memnew(SpinBox); new_size_spin_box->set_max(16384); new_size_spin_box->connect(SceneStringName(value_changed), callable_mp(this, &EditorInspectorArray::_new_size_spin_box_value_changed)); - new_size_spin_box->get_line_edit()->connect("text_submitted", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_text_submitted)); + new_size_spin_box->get_line_edit()->connect(SceneStringName(text_submitted), callable_mp(this, &EditorInspectorArray::_new_size_spin_box_text_submitted)); new_size_spin_box->set_editable(!read_only); resize_dialog_vbox->add_margin_child(TTRC("New Size:"), new_size_spin_box); @@ -2619,7 +2619,7 @@ EditorPaginator::EditorPaginator() { add_child(prev_page_button); page_line_edit = memnew(LineEdit); - page_line_edit->connect("text_submitted", callable_mp(this, &EditorPaginator::_page_line_edit_text_submitted)); + page_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPaginator::_page_line_edit_text_submitted)); page_line_edit->add_theme_constant_override("minimum_character_width", 2); add_child(page_line_edit); @@ -4680,6 +4680,7 @@ EditorInspector::EditorInspector() { search_box = nullptr; _prop_edited = "property_edited"; set_process(false); + set_focus_mode(FocusMode::FOCUS_ALL); property_focusable = -1; property_clipboard = Variant(); @@ -4698,4 +4699,6 @@ EditorInspector::EditorInspector() { // `use_settings_name_style` is true by default, set the name style accordingly. set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style()); + + set_draw_focus_border(true); } diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index 7fd694aa7fd..eeae15b1bb0 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -235,6 +235,7 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in ERR_FAIL_NULL_MSG(p_scene, "The provided scene is null."); ERR_FAIL_COND_MSG(p_scene->is_inside_tree(), "The scene must not be inside the tree."); ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be called from the editor."); + ERR_FAIL_NULL_MSG(EditorNode::get_singleton(), "EditorNode doesn't exist."); SubViewport *sub_viewport_node = memnew(SubViewport); AABB scene_aabb; diff --git a/editor/editor_log.h b/editor/editor_log.h index dfebfcb3a78..21d5b578698 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -100,8 +100,6 @@ class EditorLog : public HBoxContainer { toggle_button->set_pressed(true); toggle_button->set_text(itos(message_count)); toggle_button->set_tooltip_text(TTR(p_tooltip)); - // Don't tint the icon even when in "pressed" state. - toggle_button->add_theme_color_override("icon_color_pressed", Color(1, 1, 1, 1)); toggle_button->set_focus_mode(FOCUS_NONE); // When toggled call the callback and pass the MessageType this button is for. toggle_button->connect(SceneStringName(toggled), p_toggled_callback.bind(type)); @@ -147,11 +145,10 @@ class EditorLog : public HBoxContainer { Button *show_search_button = nullptr; LineEdit *search_box = nullptr; - // Reference to the "Output" button on the toolbar so we can update it's icon when - // Warnings or Errors are encounetered. + // Reference to the "Output" button on the toolbar so we can update its icon when warnings or errors are encountered. Button *tool_button = nullptr; - bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try trigger a save, which happens during loading). + bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try to trigger a save, which happens during loading). Timer *save_state_timer = nullptr; static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type); diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp index 6da01015ac7..608ccd6a1f0 100644 --- a/editor/editor_paths.cpp +++ b/editor/editor_paths.cpp @@ -56,6 +56,10 @@ String EditorPaths::get_cache_dir() const { return cache_dir; } +String EditorPaths::get_temp_dir() const { + return temp_dir; +} + String EditorPaths::get_project_data_dir() const { return project_data_dir; } @@ -107,6 +111,7 @@ void EditorPaths::create() { void EditorPaths::free() { ERR_FAIL_NULL(singleton); memdelete(singleton); + singleton = nullptr; } void EditorPaths::_bind_methods() { @@ -162,6 +167,7 @@ EditorPaths::EditorPaths() { config_dir = data_dir; cache_path = exe_path; cache_dir = data_dir.path_join("cache"); + temp_dir = data_dir.path_join("temp"); } else { // Typically XDG_DATA_HOME or %APPDATA%. data_path = OS::get_singleton()->get_data_path(); @@ -176,6 +182,7 @@ EditorPaths::EditorPaths() { } else { cache_dir = cache_path.path_join(OS::get_singleton()->get_godot_dir_name()); } + temp_dir = OS::get_singleton()->get_temp_path(); } paths_valid = (!data_path.is_empty() && !config_path.is_empty() && !cache_path.is_empty()); diff --git a/editor/editor_paths.h b/editor/editor_paths.h index f781330beaa..b1f6a7c032a 100644 --- a/editor/editor_paths.h +++ b/editor/editor_paths.h @@ -44,6 +44,7 @@ class EditorPaths : public Object { String data_dir; // Editor data (templates, shader cache, etc.). String config_dir; // Editor config (settings, profiles, themes, etc.). String cache_dir; // Editor cache (thumbnails, tmp generated files). + String temp_dir; // Editor temporary directory. String project_data_dir; // Project-specific data (metadata, shader cache, etc.). bool self_contained = false; // Self-contained means everything goes to `editor_data` dir. String self_contained_file; // Self-contained file with configuration. @@ -63,6 +64,7 @@ class EditorPaths : public Object { String get_data_dir() const; String get_config_dir() const; String get_cache_dir() const; + String get_temp_dir() const; String get_project_data_dir() const; String get_export_templates_dir() const; String get_debug_keystore_path() const; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index bb309a1e3c1..b7eeb1e3963 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -140,7 +140,7 @@ EditorPropertyText::EditorPropertyText() { add_focusable(text); text->set_h_size_flags(SIZE_EXPAND_FILL); text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyText::_text_changed)); - text->connect("text_submitted", callable_mp(this, &EditorPropertyText::_text_submitted)); + text->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyText::_text_submitted)); } ///////////////////// MULTILINE TEXT ///////////////////////// @@ -380,7 +380,7 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() { custom_value_edit = memnew(LineEdit); custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL); edit_custom_layout->add_child(custom_value_edit); - custom_value_edit->connect("text_submitted", callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted)); + custom_value_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted)); accept_button = memnew(Button); accept_button->set_flat(true); @@ -445,7 +445,7 @@ EditorPropertyLocale::EditorPropertyLocale() { add_child(locale_hb); locale = memnew(LineEdit); locale_hb->add_child(locale); - locale->connect("text_submitted", callable_mp(this, &EditorPropertyLocale::_locale_selected)); + locale->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyLocale::_locale_selected)); locale->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyLocale::_locale_focus_exited)); locale->set_h_size_flags(SIZE_EXPAND_FILL); @@ -600,7 +600,7 @@ EditorPropertyPath::EditorPropertyPath() { SET_DRAG_FORWARDING_CDU(path, EditorPropertyPath); path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); path_hb->add_child(path); - path->connect("text_submitted", callable_mp(this, &EditorPropertyPath::_path_selected)); + path->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyPath::_path_selected)); path->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyPath::_path_focus_exited)); path->set_h_size_flags(SIZE_EXPAND_FILL); @@ -2949,7 +2949,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() { edit->set_h_size_flags(SIZE_EXPAND_FILL); edit->hide(); edit->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyNodePath::_accept_text)); - edit->connect(SNAME("text_submitted"), callable_mp(this, &EditorPropertyNodePath::_text_submitted)); + edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyNodePath::_text_submitted)); hbc->add_child(edit); } @@ -3284,6 +3284,8 @@ void EditorPropertyResource::update_property() { sub_inspector->set_read_only(is_read_only()); sub_inspector->set_use_folding(is_using_folding()); + sub_inspector->set_draw_focus_border(false); + sub_inspector->set_mouse_filter(MOUSE_FILTER_STOP); add_child(sub_inspector); set_bottom_editor(sub_inspector); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index abcddf95513..670ff2602cb 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -1361,7 +1361,7 @@ void EditorAudioStreamPicker::_preview_draw() { } stream_preview_rect->draw_texture(icon, Point2i(EDSCALE * 4, rect.position.y + (rect.size.height - icon->get_height()) / 2), icon_modulate); - stream_preview_rect->draw_string(font, Point2i(EDSCALE * 4 + icon->get_width(), rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, size.width - 4 * EDSCALE - icon->get_width(), font_size, get_theme_color(SNAME("font_color"), EditorStringName(Editor))); + stream_preview_rect->draw_string(font, Point2i(EDSCALE * 4 + icon->get_width(), rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, size.width - 4 * EDSCALE - icon->get_width(), font_size, get_theme_color(SceneStringName(font_color), EditorStringName(Editor))); } EditorAudioStreamPicker::EditorAudioStreamPicker() : diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 41b872bf5dc..470b9d244a4 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -98,12 +98,12 @@ void EditorResourcePreviewGenerator::_bind_methods() { EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() { } -void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewport) const { +void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewport) { Callable request_vp_update_once = callable_mp(RS::get_singleton(), &RS::viewport_set_update_mode).bind(p_viewport, RS::VIEWPORT_UPDATE_ONCE); if (EditorResourcePreview::get_singleton()->is_threaded()) { RS::get_singleton()->connect(SNAME("frame_pre_draw"), request_vp_update_once, Object::CONNECT_ONE_SHOT); - RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast(this), &EditorResourcePreviewGenerator::DrawRequester::_post_semaphore)); + RS::get_singleton()->request_frame_drawn_callback(callable_mp(this, &EditorResourcePreviewGenerator::DrawRequester::_post_semaphore)); semaphore.wait(); } else { @@ -121,13 +121,13 @@ void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewp } } -void EditorResourcePreviewGenerator::DrawRequester::abort() const { +void EditorResourcePreviewGenerator::DrawRequester::abort() { if (EditorResourcePreview::get_singleton()->is_threaded()) { semaphore.post(); } } -Variant EditorResourcePreviewGenerator::DrawRequester::_post_semaphore() const { +Variant EditorResourcePreviewGenerator::DrawRequester::_post_semaphore() { semaphore.post(); return Variant(); // Needed because of how the callback is used. } diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 91c8c794194..04dc381ad4c 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -56,11 +56,11 @@ class EditorResourcePreviewGenerator : public RefCounted { class DrawRequester : public Object { Semaphore semaphore; - Variant _post_semaphore() const; + Variant _post_semaphore(); public: - void request_and_wait(RID p_viewport) const; - void abort() const; + void request_and_wait(RID p_viewport); + void abort(); }; public: diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 849add37462..8aba8c47152 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -972,6 +972,7 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { /* Debugger/profiler */ EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_NONE, "debugger/auto_switch_to_remote_scene_tree", false, "") + EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_NONE, "debugger/auto_switch_to_stack_trace", true, "") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_frame_history_size", 3600, "60,10000,1") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_frame_max_functions", 64, "16,512,1") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_target_fps", 60, "1,1000,1") diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index aecadf027c2..b6690dbdc68 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -902,6 +902,26 @@ String EditorExportPlatform::_get_script_encryption_key(const Refget_script_encryption_key().to_lower(); } +Dictionary EditorExportPlatform::get_internal_export_files() { + Dictionary files; + + // Text server support data. + if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA) && (bool)GLOBAL_GET("internationalization/locale/include_text_server_data")) { + String ts_name = TS->get_support_data_filename(); + if (!ts_name.is_empty()) { + ts_name = "res://" + ts_name; + if (!FileAccess::exists(ts_name)) { // Do not include if user supplied data file exist. + const PackedByteArray &ts_data = TS->get_support_data(); + if (!ts_data.is_empty()) { + files[ts_name] = ts_data; + } + } + } + } + + return files; +} + Vector EditorExportPlatform::get_forced_export_files() { Vector files; @@ -925,25 +945,13 @@ Vector EditorExportPlatform::get_forced_export_files() { files.push_back(extension_list_config_file); } - // Store text server data if it is supported. - if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA)) { - bool use_data = GLOBAL_GET("internationalization/locale/include_text_server_data"); - if (use_data) { - // Try using user provided data file. - if (!TS->get_support_data_filename().is_empty()) { - String ts_data = "res://" + TS->get_support_data_filename(); - if (FileAccess::exists(ts_data)) { - files.push_back(ts_data); - } else { - // Use default text server data. - String abs_path = ProjectSettings::get_singleton()->globalize_path(ts_data); - ERR_FAIL_COND_V(!TS->save_support_data(abs_path), files); - if (FileAccess::exists(abs_path)) { - files.push_back(ts_data); - // Remove the file later. - callable_mp_static(DirAccess::remove_absolute).call_deferred(abs_path); - } - } + // Text server support data. + if (TS->has_feature(TextServer::FEATURE_USE_SUPPORT_DATA) && (bool)GLOBAL_GET("internationalization/locale/include_text_server_data")) { + String ts_name = TS->get_support_data_filename(); + if (!ts_name.is_empty()) { + ts_name = "res://" + ts_name; + if (FileAccess::exists(ts_name)) { // Include user supplied data file. + files.push_back(ts_name); } } } @@ -1512,6 +1520,15 @@ Error EditorExportPlatform::export_project_files(const Ref & } } + Dictionary int_export = get_internal_export_files(); + for (const Variant &int_name : int_export.keys()) { + const PackedByteArray &array = int_export[int_name]; + err = p_save_func(p_udata, int_name, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); + if (err != OK) { + return err; + } + } + String config_file = "project.binary"; String engine_cfb = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp" + config_file); ProjectSettings::get_singleton()->save_custom(engine_cfb, custom_map, custom_list); @@ -2429,6 +2446,7 @@ void EditorExportPlatform::_bind_methods() { ClassDB::bind_method(D_METHOD("ssh_push_to_remote", "host", "port", "scp_args", "src_file", "dst_file"), &EditorExportPlatform::ssh_push_to_remote); ClassDB::bind_static_method("EditorExportPlatform", D_METHOD("get_forced_export_files"), &EditorExportPlatform::get_forced_export_files); + ClassDB::bind_static_method("EditorExportPlatform", D_METHOD("get_internal_export_files"), &EditorExportPlatform::get_internal_export_files); BIND_ENUM_CONSTANT(EXPORT_MESSAGE_NONE); BIND_ENUM_CONSTANT(EXPORT_MESSAGE_INFO); diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 9807c56d2b9..2bbe61e02d5 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -279,6 +279,7 @@ class EditorExportPlatform : public RefCounted { return worst_type; } + static Dictionary get_internal_export_files(); static Vector get_forced_export_files(); virtual bool fill_log_messages(RichTextLabel *p_log, Error p_err); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index b7cd277ce8c..686b3eac6d4 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -707,6 +707,12 @@ void ProjectExportDialog::_duplicate_preset() { preset->set_exclude_filter(current->get_exclude_filter()); preset->set_patches(current->get_patches()); preset->set_custom_features(current->get_custom_features()); + preset->set_enc_in_filter(current->get_enc_in_filter()); + preset->set_enc_ex_filter(current->get_enc_ex_filter()); + preset->set_enc_pck(current->get_enc_pck()); + preset->set_enc_directory(current->get_enc_directory()); + preset->set_script_encryption_key(current->get_script_encryption_key()); + preset->set_script_export_mode(current->get_script_export_mode()); for (const KeyValue &E : current->get_values()) { preset->set(E.key, E.value); @@ -1247,10 +1253,10 @@ void ProjectExportDialog::_validate_export_path(const String &p_path) { if (invalid_path) { export_project->get_ok_button()->set_disabled(true); - export_project->get_line_edit()->disconnect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); + export_project->get_line_edit()->disconnect(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted)); } else { export_project->get_ok_button()->set_disabled(false); - export_project->get_line_edit()->connect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); + export_project->get_line_edit()->connect(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted)); } } @@ -1283,9 +1289,9 @@ void ProjectExportDialog::_export_project() { // with _validate_export_path. // FIXME: This is a hack, we should instead change EditorFileDialog to allow // disabling validation by the "text_submitted" signal. - if (!export_project->get_line_edit()->is_connected("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted))) { + if (!export_project->get_line_edit()->is_connected(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted))) { export_project->get_ok_button()->set_disabled(false); - export_project->get_line_edit()->connect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); + export_project->get_line_edit()->connect(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted)); } export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 0cd70fcbbec..137d86d6bb6 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -182,7 +182,7 @@ FileSystemList::FileSystemList() { line_editor = memnew(LineEdit); line_editor->set_v_size_flags(SIZE_EXPAND_FILL); popup_editor_vb->add_child(line_editor); - line_editor->connect("text_submitted", callable_mp(this, &FileSystemList::_line_editor_submit)); + line_editor->connect(SceneStringName(text_submitted), callable_mp(this, &FileSystemList::_line_editor_submit)); popup_editor->connect("popup_hide", callable_mp(this, &FileSystemList::_text_editor_popup_modal_close)); } @@ -454,6 +454,10 @@ void FileSystemDock::_update_tree(const Vector &p_uncollapsed_paths, boo // Create the remaining of the tree. _create_tree(root, EditorFileSystem::get_singleton()->get_filesystem(), uncollapsed_paths, p_select_in_favorites, p_unfold_path); + if (!searched_tokens.is_empty()) { + _update_filtered_items(); + } + tree->ensure_cursor_is_visible(); updating_tree = false; } @@ -514,7 +518,7 @@ void FileSystemDock::_notification(int p_what) { button_hist_prev->connect(SceneStringName(pressed), callable_mp(this, &FileSystemDock::_bw_history)); file_list_popup->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_file_list_rmb_option)); tree_popup->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_tree_rmb_option)); - current_path_line_edit->connect("text_submitted", callable_mp(this, &FileSystemDock::_navigate_to_path).bind(false)); + current_path_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &FileSystemDock::_navigate_to_path).bind(false)); always_show_folders = bool(EDITOR_GET("docks/filesystem/always_show_folders")); @@ -1193,7 +1197,7 @@ HashSet FileSystemDock::_get_valid_conversions_for_file_paths(const Vect return all_valid_conversion_to_targets; } -void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorites) { +void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorites, bool p_navigate) { String fpath = p_path; if (fpath.ends_with("/")) { // Ignore a directory. @@ -1260,7 +1264,9 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit EditorNode::get_singleton()->load_resource(fpath); } } - _navigate_to_path(fpath, p_select_in_favorites); + if (p_navigate) { + _navigate_to_path(fpath, p_select_in_favorites); + } } void FileSystemDock::_tree_activate_file() { @@ -1274,7 +1280,7 @@ void FileSystemDock::_tree_activate_file() { bool collapsed = selected->is_collapsed(); selected->set_collapsed(!collapsed); } else { - _select_file(file_path, is_favorite && !file_path.ends_with("/")); + _select_file(file_path, is_favorite && !file_path.ends_with("/"), false); } } } @@ -3283,7 +3289,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect if (cached_valid_conversion_targets.size() > CONVERSION_SUBMENU_THRESHOLD) { container_menu = memnew(PopupMenu); - container_menu->connect("id_pressed", callable_mp(this, &FileSystemDock::_generic_rmb_option_selected)); + container_menu->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_generic_rmb_option_selected)); p_popup->add_submenu_node_item(TTR("Convert to..."), container_menu, FILE_NEW); conversion_string_template = "%s"; diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index c953dc435c3..54dc47dda8d 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -264,7 +264,7 @@ class FileSystemDock : public VBoxContainer { void _set_file_display(bool p_active); void _fs_changed(); - void _select_file(const String &p_path, bool p_select_in_favorites = false); + void _select_file(const String &p_path, bool p_select_in_favorites = false, bool p_navigate = true); void _tree_activate_file(); void _file_list_activate_file(int p_idx); void _file_multi_selected(int p_index, bool p_selected); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 3a76a0ebc67..e12db663c82 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -322,7 +322,7 @@ FindInFilesDialog::FindInFilesDialog() { _search_text_line_edit = memnew(LineEdit); _search_text_line_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); _search_text_line_edit->connect(SceneStringName(text_changed), callable_mp(this, &FindInFilesDialog::_on_search_text_modified)); - _search_text_line_edit->connect("text_submitted", callable_mp(this, &FindInFilesDialog::_on_search_text_submitted)); + _search_text_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &FindInFilesDialog::_on_search_text_submitted)); gc->add_child(_search_text_line_edit); _replace_label = memnew(Label); @@ -332,7 +332,7 @@ FindInFilesDialog::FindInFilesDialog() { _replace_text_line_edit = memnew(LineEdit); _replace_text_line_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); - _replace_text_line_edit->connect("text_submitted", callable_mp(this, &FindInFilesDialog::_on_replace_text_submitted)); + _replace_text_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &FindInFilesDialog::_on_replace_text_submitted)); _replace_text_line_edit->hide(); gc->add_child(_replace_text_line_edit); diff --git a/editor/group_settings_editor.cpp b/editor/group_settings_editor.cpp index 14218cd3d2c..5d5a82bc73d 100644 --- a/editor/group_settings_editor.cpp +++ b/editor/group_settings_editor.cpp @@ -502,7 +502,7 @@ GroupSettingsEditor::GroupSettingsEditor() { group_name->set_h_size_flags(SIZE_EXPAND_FILL); group_name->set_clear_button_enabled(true); group_name->connect(SceneStringName(text_changed), callable_mp(this, &GroupSettingsEditor::_group_name_text_changed)); - group_name->connect("text_submitted", callable_mp(this, &GroupSettingsEditor::_text_submitted)); + group_name->connect(SceneStringName(text_submitted), callable_mp(this, &GroupSettingsEditor::_text_submitted)); hbc->add_child(group_name); l = memnew(Label); @@ -512,7 +512,7 @@ GroupSettingsEditor::GroupSettingsEditor() { group_description = memnew(LineEdit); group_description->set_clear_button_enabled(true); group_description->set_h_size_flags(SIZE_EXPAND_FILL); - group_description->connect("text_submitted", callable_mp(this, &GroupSettingsEditor::_text_submitted)); + group_description->connect(SceneStringName(text_submitted), callable_mp(this, &GroupSettingsEditor::_text_submitted)); hbc->add_child(group_description); add_button = memnew(Button); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index c74ca13b833..6498a533ad9 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -119,25 +119,37 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector &p_file selected_options = p_selected_options; String f = files[0]; + + filter->select(p_filter); + dir->set_text(f.get_base_dir()); + file->set_text(f.get_file()); + _dir_submitted(f.get_base_dir()); + if (mode == FILE_MODE_OPEN_FILES) { emit_signal(SNAME("files_selected"), files); } else { if (mode == FILE_MODE_SAVE_FILE) { - if (p_filter >= 0 && p_filter < filters.size()) { + if (p_filter != 0 && p_filter != filter->get_item_count() - 1) { bool valid = false; - String flt = filters[p_filter].get_slice(";", 0); - int filter_slice_count = flt.get_slice_count(","); - for (int j = 0; j < filter_slice_count; j++) { - String str = (flt.get_slice(",", j).strip_edges()); - if (f.match(str)) { - valid = true; - break; - } + int idx = p_filter; + if (filters.size() > 1) { + idx--; } + if (idx >= 0 && idx < filters.size()) { + String flt = filters[idx].get_slice(";", 0); + int filter_slice_count = flt.get_slice_count(","); + for (int j = 0; j < filter_slice_count; j++) { + String str = (flt.get_slice(",", j).strip_edges()); + if (f.match(str)) { + valid = true; + break; + } + } - if (!valid && filter_slice_count > 0) { - String str = (flt.get_slice(",", 0).strip_edges()); - f += str.substr(1, str.length() - 1); + if (!valid && filter_slice_count > 0) { + String str = (flt.get_slice(",", 0).strip_edges()); + f += str.substr(1, str.length() - 1); + } } } emit_signal(SNAME("file_selected"), f); @@ -147,9 +159,6 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector &p_file emit_signal(SNAME("dir_selected"), f); } } - file->set_text(f); - dir->set_text(f.get_base_dir()); - filter->select(p_filter); } void EditorFileDialog::popup_file_dialog() { @@ -177,12 +186,14 @@ void EditorFileDialog::_update_theme_item_cache() { theme_cache.back_folder = get_editor_theme_icon(SNAME("Back")); theme_cache.reload = get_editor_theme_icon(SNAME("Reload")); theme_cache.toggle_hidden = get_editor_theme_icon(SNAME("GuiVisibilityVisible")); + theme_cache.toggle_filename_filter = get_editor_theme_icon(SNAME("FilenameFilter")); theme_cache.favorite = get_editor_theme_icon(SNAME("Favorites")); theme_cache.mode_thumbnails = get_editor_theme_icon(SNAME("FileThumbnail")); theme_cache.mode_list = get_editor_theme_icon(SNAME("FileList")); theme_cache.favorites_up = get_editor_theme_icon(SNAME("MoveUp")); theme_cache.favorites_down = get_editor_theme_icon(SNAME("MoveDown")); theme_cache.create_folder = get_editor_theme_icon(SNAME("FolderCreate")); + theme_cache.open_folder = get_editor_theme_icon(SNAME("FolderBrowse")); theme_cache.filter_box = get_editor_theme_icon(SNAME("Search")); theme_cache.file_sort_button = get_editor_theme_icon(SNAME("Sort")); @@ -331,6 +342,7 @@ void EditorFileDialog::shortcut_input(const Ref &p_event) { handled = true; } if (ED_IS_SHORTCUT("file_dialog/focus_filter", p_event)) { + show_search_filter_button->set_pressed(!show_search_filter_button->is_pressed()); _focus_filter_box(); handled = true; } @@ -365,6 +377,7 @@ Vector EditorFileDialog::get_selected_files() const { } void EditorFileDialog::update_dir() { + full_dir = dir_access->get_current_dir(); if (drives->is_visible()) { if (dir_access->get_current_dir().is_network_share_path()) { _update_drives(false); @@ -531,7 +544,7 @@ void EditorFileDialog::_action_pressed() { String file_text = file->get_text(); String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().path_join(file_text); - if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) { + if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && (dir_access->file_exists(f) || dir_access->is_bundle(f))) { _save_to_recent(); hide(); emit_signal(SNAME("file_selected"), f); @@ -567,7 +580,7 @@ void EditorFileDialog::_action_pressed() { String flt = filters[i].get_slice(";", 0); for (int j = 0; j < flt.get_slice_count(","); j++) { String str = flt.get_slice(",", j).strip_edges(); - if (f.match(str)) { + if (f.matchn(str)) { valid = true; break; } @@ -583,16 +596,16 @@ void EditorFileDialog::_action_pressed() { } if (idx >= 0 && idx < filters.size()) { String flt = filters[idx].get_slice(";", 0); - int filterSliceCount = flt.get_slice_count(","); - for (int j = 0; j < filterSliceCount; j++) { + int filter_slice_count = flt.get_slice_count(","); + for (int j = 0; j < filter_slice_count; j++) { String str = (flt.get_slice(",", j).strip_edges()); - if (f.match(str)) { + if (f.matchn(str)) { valid = true; break; } } - if (!valid && filterSliceCount > 0) { + if (!valid && filter_slice_count > 0) { String str = (flt.get_slice(",", 0).strip_edges()); f += str.substr(1, str.length() - 1); _request_single_thumbnail(get_current_dir().path_join(f.get_file())); @@ -783,6 +796,12 @@ void EditorFileDialog::_item_list_item_rmb_clicked(int p_item, const Vector2 &p_ item_menu->add_icon_item(theme_cache.filesystem, item_text, ITEM_MENU_SHOW_IN_EXPLORER); } #endif + if (single_item_selected) { + Dictionary item_meta = item_list->get_item_metadata(p_item); + if (item_meta["bundle"]) { + item_menu->add_icon_item(theme_cache.open_folder, TTR("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT); + } + } if (item_menu->get_item_count() > 0) { item_menu->set_position(item_list->get_screen_position() + p_pos); @@ -845,7 +864,7 @@ void EditorFileDialog::_item_menu_id_pressed(int p_option) { case ITEM_MENU_SHOW_IN_EXPLORER: { String path; int idx = item_list->get_current(); - if (idx == -1 || item_list->get_selected_items().size() == 0) { + if (idx == -1 || !item_list->is_anything_selected()) { // Folder background was clicked. Open this folder. path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir()); } else { @@ -855,6 +874,20 @@ void EditorFileDialog::_item_menu_id_pressed(int p_option) { } OS::get_singleton()->shell_show_in_file_manager(path, true); } break; + + case ITEM_MENU_SHOW_BUNDLE_CONTENT: { + String path; + int idx = item_list->get_current(); + if (idx == -1 || !item_list->is_anything_selected()) { + return; + } + Dictionary item_meta = item_list->get_item_metadata(idx); + dir_access->change_dir(item_meta["path"]); + callable_mp(this, &EditorFileDialog::update_file_list).call_deferred(); + callable_mp(this, &EditorFileDialog::update_dir).call_deferred(); + + _push_history(); + } break; } } @@ -1022,28 +1055,6 @@ void EditorFileDialog::update_file_list() { } sort_file_info_list(file_infos, file_sort); - while (!dirs.is_empty()) { - const String &dir_name = dirs.front()->get(); - - item_list->add_item(dir_name); - - if (display_mode == DISPLAY_THUMBNAILS) { - item_list->set_item_icon(-1, folder_thumbnail); - } else { - item_list->set_item_icon(-1, theme_cache.folder); - } - - Dictionary d; - d["name"] = dir_name; - d["path"] = cdir.path_join(dir_name); - d["dir"] = true; - - item_list->set_item_metadata(-1, d); - item_list->set_item_icon_modulate(-1, get_dir_icon_color(String(d["path"]))); - - dirs.pop_front(); - } - List patterns; // build filter if (filter->get_selected() == filter->get_item_count() - 1) { @@ -1070,6 +1081,44 @@ void EditorFileDialog::update_file_list() { } } + while (!dirs.is_empty()) { + const String &dir_name = dirs.front()->get(); + + bool bundle = dir_access->is_bundle(dir_name); + bool found = true; + if (bundle) { + bool match = patterns.is_empty(); + for (const String &E : patterns) { + if (dir_name.matchn(E)) { + match = true; + break; + } + } + found = match; + } + + if (found) { + item_list->add_item(dir_name); + + if (display_mode == DISPLAY_THUMBNAILS) { + item_list->set_item_icon(-1, folder_thumbnail); + } else { + item_list->set_item_icon(-1, theme_cache.folder); + } + + Dictionary d; + d["name"] = dir_name; + d["path"] = cdir.path_join(dir_name); + d["dir"] = !bundle; + d["bundle"] = bundle; + + item_list->set_item_metadata(-1, d); + item_list->set_item_icon_modulate(-1, get_dir_icon_color(String(d["path"]))); + } + + dirs.pop_front(); + } + while (!file_infos.is_empty()) { bool match = patterns.is_empty(); @@ -1105,6 +1154,7 @@ void EditorFileDialog::update_file_list() { Dictionary d; d["name"] = file_info.name; d["dir"] = false; + d["bundle"] = false; d["path"] = file_info.path; item_list->set_item_metadata(-1, d); @@ -1148,6 +1198,15 @@ void EditorFileDialog::_filter_selected(int) { update_file_list(); } +void EditorFileDialog::_search_filter_selected() { + Vector items = item_list->get_selected_items(); + if (!items.is_empty()) { + int index = items[0]; + file->set_text(item_list->get_item_text(index)); + file->emit_signal(SceneStringName(text_submitted), file->get_text()); + } +} + void EditorFileDialog::update_filters() { filter->clear(); processed_filters.clear(); @@ -1195,7 +1254,7 @@ void EditorFileDialog::update_filters() { } } - String f = TTR("All Files (*)"); + String f = TTR("All Files") + " (*.*)"; filter->add_item(f); processed_filters.push_back("*.*;" + f); } @@ -1206,6 +1265,23 @@ void EditorFileDialog::clear_filters() { invalidate(); } +void EditorFileDialog::clear_search_filter() { + set_search_filter(""); + update_search_filter_gui(); + invalidate(); +} + +void EditorFileDialog::update_search_filter_gui() { + filter_hb->set_visible(show_search_filter); + if (!show_search_filter) { + search_string.clear(); + } + if (filter_box->get_text() == search_string) { + return; + } + filter_box->set_text(search_string); +} + void EditorFileDialog::add_filter(const String &p_filter, const String &p_description) { if (p_description.is_empty()) { filters.push_back(p_filter); @@ -1225,10 +1301,24 @@ void EditorFileDialog::set_filters(const Vector &p_filters) { invalidate(); } +void EditorFileDialog::set_search_filter(const String &p_search_filter) { + if (search_string == p_search_filter) { + return; + } + search_string = p_search_filter; + update_search_filter_gui(); + emit_signal(SNAME("filename_filter_changed"), filter); + invalidate(); +} + Vector EditorFileDialog::get_filters() const { return filters; } +String EditorFileDialog::get_search_filter() const { + return search_string; +} + String EditorFileDialog::get_current_dir() const { return dir_access->get_current_dir(); } @@ -1418,6 +1508,11 @@ void EditorFileDialog::_focus_filter_box() { void EditorFileDialog::_filter_changed(const String &p_text) { search_string = p_text; invalidate(); + + item_list->deselect_all(); + if (item_list->get_item_count() > 0) { + item_list->call_deferred("select", 0); + } } void EditorFileDialog::_file_sort_popup(int p_id) { @@ -1521,6 +1616,7 @@ void EditorFileDialog::_update_icons() { filter_box->set_right_icon(theme_cache.filter_box); file_sort_button->set_button_icon(theme_cache.file_sort_button); + show_search_filter_button->set_button_icon(theme_cache.toggle_filename_filter); filter_box->set_clear_button_enabled(true); fav_up->set_button_icon(theme_cache.favorites_up); @@ -2000,6 +2096,9 @@ void EditorFileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_option_count"), &EditorFileDialog::get_option_count); ClassDB::bind_method(D_METHOD("add_option", "name", "values", "default_value_index"), &EditorFileDialog::add_option); ClassDB::bind_method(D_METHOD("get_selected_options"), &EditorFileDialog::get_selected_options); + ClassDB::bind_method(D_METHOD("clear_filename_filter"), &EditorFileDialog::clear_search_filter); + ClassDB::bind_method(D_METHOD("set_filename_filter", "filter"), &EditorFileDialog::set_search_filter); + ClassDB::bind_method(D_METHOD("get_filename_filter"), &EditorFileDialog::get_search_filter); ClassDB::bind_method(D_METHOD("get_current_dir"), &EditorFileDialog::get_current_dir); ClassDB::bind_method(D_METHOD("get_current_file"), &EditorFileDialog::get_current_file); ClassDB::bind_method(D_METHOD("get_current_path"), &EditorFileDialog::get_current_path); @@ -2028,6 +2127,7 @@ void EditorFileDialog::_bind_methods() { ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path"))); ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths"))); ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir"))); + ADD_SIGNAL(MethodInfo("filename_filter_changed", PropertyInfo(Variant::STRING, "filter"))); ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access"); ADD_PROPERTY(PropertyInfo(Variant::INT, "display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"), "set_display_mode", "get_display_mode"); @@ -2074,6 +2174,24 @@ void EditorFileDialog::set_show_hidden_files(bool p_show) { invalidate(); } +void EditorFileDialog::set_show_search_filter(bool p_show) { + if (p_show == show_search_filter) { + return; + } + if (p_show) { + filter_box->grab_focus(); + } else { + search_string.clear(); + filter_box->clear(); + if (filter_box->has_focus()) { + item_list->call_deferred("grab_focus"); + } + } + show_search_filter = p_show; + update_search_filter_gui(); + invalidate(); +} + bool EditorFileDialog::is_showing_hidden_files() const { return show_hidden_files; } @@ -2217,7 +2335,6 @@ EditorFileDialog::EditorFileDialog() { dir = memnew(LineEdit); dir->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); pathhb->add_child(dir); - dir->set_h_size_flags(Control::SIZE_EXPAND_FILL); refresh = memnew(Button); refresh->set_theme_type_variation("FlatButton"); @@ -2232,37 +2349,6 @@ EditorFileDialog::EditorFileDialog() { favorite->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_favorite_pressed)); pathhb->add_child(favorite); - show_hidden = memnew(Button); - show_hidden->set_theme_type_variation("FlatButton"); - show_hidden->set_toggle_mode(true); - show_hidden->set_pressed(is_showing_hidden_files()); - show_hidden->set_tooltip_text(TTR("Toggle the visibility of hidden files.")); - show_hidden->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::set_show_hidden_files)); - pathhb->add_child(show_hidden); - - pathhb->add_child(memnew(VSeparator)); - - Ref view_mode_group; - view_mode_group.instantiate(); - - mode_thumbnails = memnew(Button); - mode_thumbnails->set_theme_type_variation("FlatButton"); - mode_thumbnails->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::set_display_mode).bind(DISPLAY_THUMBNAILS)); - mode_thumbnails->set_toggle_mode(true); - mode_thumbnails->set_pressed(display_mode == DISPLAY_THUMBNAILS); - mode_thumbnails->set_button_group(view_mode_group); - mode_thumbnails->set_tooltip_text(TTR("View items as a grid of thumbnails.")); - pathhb->add_child(mode_thumbnails); - - mode_list = memnew(Button); - mode_list->set_theme_type_variation("FlatButton"); - mode_list->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::set_display_mode).bind(DISPLAY_LIST)); - mode_list->set_toggle_mode(true); - mode_list->set_pressed(display_mode == DISPLAY_LIST); - mode_list->set_button_group(view_mode_group); - mode_list->set_tooltip_text(TTR("View items as a list.")); - pathhb->add_child(mode_list); - shortcuts_container = memnew(HBoxContainer); pathhb->add_child(shortcuts_container); @@ -2278,6 +2364,8 @@ EditorFileDialog::EditorFileDialog() { makedir->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_make_dir)); pathhb->add_child(makedir); + dir->set_h_size_flags(Control::SIZE_EXPAND_FILL); + body_hsplit = memnew(HSplitContainer); body_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbc->add_child(body_hsplit); @@ -2349,21 +2437,55 @@ EditorFileDialog::EditorFileDialog() { l = memnew(Label(TTR("Directories & Files:"))); l->set_theme_type_variation("HeaderSmall"); + l->set_h_size_flags(Control::SIZE_EXPAND_FILL); + lower_hb->add_child(l); - list_vb->add_child(lower_hb); - preview_hb->add_child(list_vb); + show_hidden = memnew(Button); + show_hidden->set_theme_type_variation("FlatButton"); + show_hidden->set_toggle_mode(true); + show_hidden->set_pressed(is_showing_hidden_files()); + show_hidden->set_tooltip_text(TTR("Toggle the visibility of hidden files.")); + show_hidden->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::set_show_hidden_files)); + lower_hb->add_child(show_hidden); - filter_box = memnew(LineEdit); - filter_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - filter_box->set_placeholder(TTR("Filter")); - filter_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorFileDialog::_filter_changed)); - lower_hb->add_child(filter_box); + lower_hb->add_child(memnew(VSeparator)); + + Ref view_mode_group; + view_mode_group.instantiate(); + + mode_thumbnails = memnew(Button); + mode_thumbnails->set_theme_type_variation("FlatButton"); + mode_thumbnails->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::set_display_mode).bind(DISPLAY_THUMBNAILS)); + mode_thumbnails->set_toggle_mode(true); + mode_thumbnails->set_pressed(display_mode == DISPLAY_THUMBNAILS); + mode_thumbnails->set_button_group(view_mode_group); + mode_thumbnails->set_tooltip_text(TTR("View items as a grid of thumbnails.")); + lower_hb->add_child(mode_thumbnails); + + mode_list = memnew(Button); + mode_list->set_theme_type_variation("FlatButton"); + mode_list->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::set_display_mode).bind(DISPLAY_LIST)); + mode_list->set_toggle_mode(true); + mode_list->set_pressed(display_mode == DISPLAY_LIST); + mode_list->set_button_group(view_mode_group); + mode_list->set_tooltip_text(TTR("View items as a list.")); + lower_hb->add_child(mode_list); + + lower_hb->add_child(memnew(VSeparator)); file_sort_button = memnew(MenuButton); file_sort_button->set_flat(true); file_sort_button->set_tooltip_text(TTR("Sort files")); + show_search_filter_button = memnew(Button); + show_search_filter_button->set_theme_type_variation("FlatButton"); + show_search_filter_button->set_toggle_mode(true); + show_search_filter_button->set_pressed(false); + show_search_filter_button->set_tooltip_text(TTR("Toggle the visibility of the filter for file names.")); + show_search_filter_button->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::set_show_search_filter)); + lower_hb->add_child(show_search_filter_button); + PopupMenu *p = file_sort_button->get_popup(); p->connect(SceneStringName(id_pressed), callable_mp(this, &EditorFileDialog::_file_sort_popup)); p->add_radio_check_item(TTR("Sort by Name (Ascending)"), static_cast(FileSortOption::FILE_SORT_NAME)); @@ -2375,6 +2497,9 @@ EditorFileDialog::EditorFileDialog() { p->set_item_checked(0, true); lower_hb->add_child(file_sort_button); + list_vb->add_child(lower_hb); + preview_hb->add_child(list_vb); + // Item (files and folders) list with context menu. item_list = memnew(ItemList); @@ -2400,6 +2525,15 @@ EditorFileDialog::EditorFileDialog() { prev_cc->add_child(preview); preview_vb->hide(); + filter_hb = memnew(HBoxContainer); + filter_hb->add_child(memnew(Label(RTR("Filter:")))); + filter_box = memnew(LineEdit); + filter_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + filter_box->set_placeholder(TTR("Filter")); + filter_hb->add_child(filter_box); + filter_hb->set_visible(false); + item_vb->add_child(filter_hb); + file_box = memnew(HBoxContainer); l = memnew(Label(TTR("File:"))); @@ -2427,8 +2561,10 @@ EditorFileDialog::EditorFileDialog() { item_list->connect("multi_selected", callable_mp(this, &EditorFileDialog::_multi_selected), CONNECT_DEFERRED); item_list->connect("item_activated", callable_mp(this, &EditorFileDialog::_item_dc_selected).bind()); item_list->connect("empty_clicked", callable_mp(this, &EditorFileDialog::_items_clear_selection)); - dir->connect("text_submitted", callable_mp(this, &EditorFileDialog::_dir_submitted)); - file->connect("text_submitted", callable_mp(this, &EditorFileDialog::_file_submitted)); + dir->connect(SceneStringName(text_submitted), callable_mp(this, &EditorFileDialog::_dir_submitted)); + filter_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorFileDialog::_filter_changed)); + filter_box->connect(SceneStringName(text_submitted), callable_mp(this, &EditorFileDialog::_search_filter_selected).unbind(1)); + file->connect(SceneStringName(text_submitted), callable_mp(this, &EditorFileDialog::_file_submitted)); filter->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_filter_selected)); confirm_save = memnew(ConfirmationDialog); diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h index 52b5686cc02..1c86e882c47 100644 --- a/editor/gui/editor_file_dialog.h +++ b/editor/gui/editor_file_dialog.h @@ -84,7 +84,8 @@ class EditorFileDialog : public ConfirmationDialog { ITEM_MENU_DELETE, ITEM_MENU_REFRESH, ITEM_MENU_NEW_FOLDER, - ITEM_MENU_SHOW_IN_EXPLORER + ITEM_MENU_SHOW_IN_EXPLORER, + ITEM_MENU_SHOW_BUNDLE_CONTENT, }; ConfirmationDialog *makedialog = nullptr; @@ -130,8 +131,11 @@ class EditorFileDialog : public ConfirmationDialog { Button *refresh = nullptr; Button *favorite = nullptr; Button *show_hidden = nullptr; + Button *show_search_filter_button = nullptr; String search_string; + bool show_search_filter = false; + HBoxContainer *filter_hb = nullptr; LineEdit *filter_box = nullptr; FileSortOption file_sort = FileSortOption::FILE_SORT_NAME; MenuButton *file_sort_button = nullptr; @@ -166,8 +170,10 @@ class EditorFileDialog : public ConfirmationDialog { Ref parent_folder; Ref forward_folder; Ref back_folder; + Ref open_folder; Ref reload; Ref toggle_hidden; + Ref toggle_filename_filter; Ref favorite; Ref mode_thumbnails; Ref mode_list; @@ -205,10 +211,12 @@ class EditorFileDialog : public ConfirmationDialog { Vector