mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 19:42:43 +00:00
Compare commits
133 Commits
ac80078736
...
bdde9ec5a4
Author | SHA1 | Date | |
---|---|---|---|
|
bdde9ec5a4 | ||
|
84db024f6f | ||
|
9e6098432a | ||
|
cfef79415b | ||
|
7c1f42506a | ||
|
70963cf515 | ||
|
b11bb1aa97 | ||
|
285954659d | ||
|
23fc8e22a3 | ||
|
219b14b905 | ||
|
bc5e2f9b96 | ||
|
02efdb28dc | ||
|
a0cd8f187a | ||
|
650e96b768 | ||
|
3a4feeda89 | ||
|
32b4f40cc8 | ||
|
d6ec81af77 | ||
|
918faec239 | ||
|
1680edb2cd | ||
|
e4dbba94d9 | ||
|
97b3dd4cfe | ||
|
3e77646645 | ||
|
ec7fd4f6f1 | ||
|
63a408f320 | ||
|
480f616bbe | ||
|
dd0c24bcd3 | ||
|
c4a78d09e5 | ||
|
288717d7eb | ||
|
c636c86f65 | ||
|
086d1ea2ac | ||
|
8e170248b3 | ||
|
4f52c2bb1f | ||
|
2599df3b8a | ||
|
728927425f | ||
|
fd4c29a189 | ||
|
cf541f0997 | ||
|
91bd80de51 | ||
|
3ded11d0bc | ||
|
449d90b64e | ||
|
8d530bc95a | ||
|
7ac9d7fec9 | ||
|
0a50cef751 | ||
|
7a5b1ed736 | ||
|
0dda6a974c | ||
|
1dcb686325 | ||
|
8811b39968 | ||
|
895d433f50 | ||
|
cc48a22b29 | ||
|
a46abc4f15 | ||
|
8e324c4589 | ||
|
80e73b0dca | ||
|
1c1e833a43 | ||
|
778f26e69e | ||
|
2dbf195af5 | ||
|
fa29dde142 | ||
|
3d6e712177 | ||
|
1889d2a264 | ||
|
6330db475f | ||
|
d72112ba0a | ||
|
973f16aef1 | ||
|
02b2efc668 | ||
|
7fa39c0dbe | ||
|
1250681a4f | ||
|
8bb3e5360e | ||
|
0d9a705e25 | ||
|
985dc61386 | ||
|
8a8fd299f8 | ||
|
c6f4228706 | ||
|
bb3d0045b0 | ||
|
f5871c18bf | ||
|
68f638cf02 | ||
|
90b4b48b5a | ||
|
3b6705a641 | ||
|
7e3d480087 | ||
|
e283fdfb59 | ||
|
df3367f334 | ||
|
602c5edbc4 | ||
|
790efbb783 | ||
|
5efd124ca1 | ||
|
39423d99fa | ||
|
31a3b418f7 | ||
|
c810ea4c1b | ||
|
f2d58f5d76 | ||
|
4d4407ce5a | ||
|
6c05ec3d67 | ||
|
c9acbf5a79 | ||
|
177bba12b0 | ||
|
68fe5817aa | ||
|
e4608dd6c2 | ||
|
773c8e9a93 | ||
|
e9ce3932b3 | ||
|
a52e28436e | ||
|
6c9337de36 | ||
|
d9b458d4b0 | ||
|
1d5c589e71 | ||
|
03863f117f | ||
|
46d83fd149 | ||
|
f5431fe3f0 | ||
|
d4a62ace48 | ||
|
313fe08e19 | ||
|
89f97021de | ||
|
af3fb0a2b8 | ||
|
934549e2a0 | ||
|
0e4a4e3c4d | ||
|
287b7543a0 | ||
|
3a946aaa73 | ||
|
a389eb4608 | ||
|
d55ed0cb15 | ||
|
f5fad7592f | ||
|
ce833a3885 | ||
|
d1ba152197 | ||
|
5a856a6896 | ||
|
9c2ca820f2 | ||
|
f72b841390 | ||
|
121097db0a | ||
|
88b3090745 | ||
|
5be53c36c0 | ||
|
d467b3acb5 | ||
|
6e9d31f602 | ||
|
d692b7bdde | ||
|
b0e04c1e7a | ||
|
4ba533d0b5 | ||
|
186f35fc9b | ||
|
168a2a1466 | ||
|
78895c709c | ||
|
be349fa6d3 | ||
|
78fc90e81e | ||
|
1b4f6f6bfa | ||
|
cbc8468e3e | ||
|
bc30bb4fc0 | ||
|
cab80cb97d | ||
|
49b4c20f3a | ||
|
a1a5c87f9e |
10
.github/actions/godot-build/action.yml
vendored
10
.github/actions/godot-build/action.yml
vendored
@ -18,12 +18,12 @@ inputs:
|
||||
required: false
|
||||
scons-cache:
|
||||
description: The SCons cache path.
|
||||
default: ${{ github.workspace }}/.scons-cache/
|
||||
default: ${{ github.workspace }}/.scons_cache/
|
||||
scons-cache-limit:
|
||||
description: The SCons cache size limit.
|
||||
# actions/cache has 10 GiB limit, and GitHub runners have a 14 GiB disk.
|
||||
# Limit to 7 GiB to avoid having the extracted cache fill the disk.
|
||||
default: 7168
|
||||
default: 7
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@ -32,10 +32,8 @@ runs:
|
||||
shell: sh
|
||||
env:
|
||||
SCONSFLAGS: ${{ inputs.sconsflags }}
|
||||
SCONS_CACHE: ${{ inputs.scons-cache }}
|
||||
SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }}
|
||||
run: |
|
||||
echo "Building with flags:" platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }}
|
||||
echo "Building with flags:" platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }} "cache_path=${{ inputs.scons-cache }}" cache_limit=${{ inputs.scons-cache-limit }}
|
||||
|
||||
if [ "${{ inputs.target }}" != "editor" ]; then
|
||||
# Ensure we don't include editor code in export template builds.
|
||||
@ -49,5 +47,5 @@ runs:
|
||||
export BUILD_NAME="gh"
|
||||
fi
|
||||
|
||||
scons platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }}
|
||||
scons platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }} "cache_path=${{ inputs.scons-cache }}" cache_limit=${{ inputs.scons-cache-limit }}
|
||||
ls -l bin/
|
||||
|
@ -6,7 +6,7 @@ inputs:
|
||||
default: ${{ github.job }}
|
||||
scons-cache:
|
||||
description: The SCons cache path.
|
||||
default: ${{ github.workspace }}/.scons-cache/
|
||||
default: ${{ github.workspace }}/.scons_cache/
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@ -29,7 +29,6 @@ runs:
|
||||
# 4. A partial match for the same base branch only (not ideal, matches any PR with the same base branch).
|
||||
|
||||
restore-keys: |
|
||||
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}-${{ github.sha }}
|
||||
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-${{ github.ref }}
|
||||
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}-refs/heads/${{ env.GODOT_BASE_BRANCH }}
|
||||
${{ inputs.cache-name }}-${{ env.GODOT_BASE_BRANCH }}
|
||||
|
2
.github/actions/godot-cache-save/action.yml
vendored
2
.github/actions/godot-cache-save/action.yml
vendored
@ -6,7 +6,7 @@ inputs:
|
||||
default: ${{ github.job }}
|
||||
scons-cache:
|
||||
description: The SCons cache path.
|
||||
default: ${{ github.workspace }}/.scons-cache/
|
||||
default: ${{ github.workspace }}/.scons_cache/
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
|
3
.github/workflows/godot_cpp_test.yml
vendored
3
.github/workflows/godot_cpp_test.yml
vendored
@ -52,9 +52,6 @@ jobs:
|
||||
# continue-on-error: true
|
||||
|
||||
- name: Build godot-cpp test extension
|
||||
env: # Keep synced with godot-build.
|
||||
SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
|
||||
SCONS_CACHE_LIMIT: 7168
|
||||
run: scons --directory=./godot-cpp/test target=template_debug dev_build=yes verbose=yes
|
||||
|
||||
# - name: Save Godot build cache
|
||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -36,8 +36,8 @@ compile_commands.json
|
||||
platform/windows/godot_res.res
|
||||
|
||||
# Ninja build files
|
||||
build.ninja
|
||||
.ninja
|
||||
*.ninja
|
||||
.ninja/
|
||||
run_ninja_env.bat
|
||||
|
||||
# Generated by Godot binary
|
||||
@ -77,6 +77,9 @@ venv
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Python modules
|
||||
.*_cache/
|
||||
|
||||
# Documentation
|
||||
doc/_build/
|
||||
|
||||
@ -164,9 +167,6 @@ gmon.out
|
||||
# Kdevelop
|
||||
*.kdev4
|
||||
|
||||
# Mypy
|
||||
.mypy_cache
|
||||
|
||||
# Qt Creator
|
||||
*.config
|
||||
*.creator
|
||||
|
44
SConstruct
44
SConstruct
@ -271,6 +271,8 @@ opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
|
||||
opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
|
||||
opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the Project Manager", True))
|
||||
opts.Add(BoolVariable("steamapi", "Enable minimal SteamAPI integration for usage time tracking (editor only)", False))
|
||||
opts.Add("cache_path", "Path to a directory where SCons cache files will be stored. No value disables the cache.", "")
|
||||
opts.Add("cache_limit", "Max size (in GiB) for the SCons cache. 0 means no limit.", "0")
|
||||
|
||||
# Thirdparty libraries
|
||||
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
|
||||
@ -321,6 +323,9 @@ opts.Add("rcflags", "Custom flags for Windows resource compiler")
|
||||
# in following code (especially platform and custom_modules).
|
||||
opts.Update(env)
|
||||
|
||||
# Setup caching logic early to catch everything.
|
||||
methods.prepare_cache(env)
|
||||
|
||||
# Copy custom environment variables if set.
|
||||
if env["import_env_vars"]:
|
||||
for env_var in str(env["import_env_vars"]).split(","):
|
||||
@ -354,7 +359,9 @@ if env["platform"] == "":
|
||||
if env["platform"] in compatibility_platform_aliases:
|
||||
alias = env["platform"]
|
||||
platform = compatibility_platform_aliases[alias]
|
||||
print_warning(f'Platform "{alias}" has been renamed to "{platform}" in Godot 4. Building for platform "{platform}".')
|
||||
print_warning(
|
||||
f'Platform "{alias}" has been renamed to "{platform}" in Godot 4. Building for platform "{platform}".'
|
||||
)
|
||||
env["platform"] = platform
|
||||
|
||||
# Alias for convenience.
|
||||
@ -656,40 +663,32 @@ elif methods.using_gcc(env):
|
||||
"to switch to posix threads."
|
||||
)
|
||||
Exit(255)
|
||||
if env["debug_paths_relative"] and cc_version_major < 8:
|
||||
print_warning("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
|
||||
env["debug_paths_relative"] = False
|
||||
elif methods.using_clang(env):
|
||||
# Apple LLVM versions differ from upstream LLVM version \o/, compare
|
||||
# in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
|
||||
if env["platform"] == "macos" or env["platform"] == "ios":
|
||||
vanilla = methods.is_vanilla_clang(env)
|
||||
if vanilla and cc_version_major < 6:
|
||||
print_error(
|
||||
"Detected Clang version older than 6, which does not fully support "
|
||||
"C++17. Supported versions are Clang 6 and later."
|
||||
)
|
||||
Exit(255)
|
||||
elif not vanilla and cc_version_major < 10:
|
||||
if methods.is_apple_clang(env):
|
||||
if cc_version_major < 10:
|
||||
print_error(
|
||||
"Detected Apple Clang version older than 10, which does not fully "
|
||||
"support C++17. Supported versions are Apple Clang 10 and later."
|
||||
)
|
||||
Exit(255)
|
||||
if env["debug_paths_relative"] and not vanilla and cc_version_major < 12:
|
||||
elif env["debug_paths_relative"] and cc_version_major < 12:
|
||||
print_warning(
|
||||
"Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option."
|
||||
)
|
||||
env["debug_paths_relative"] = False
|
||||
elif cc_version_major < 6:
|
||||
else:
|
||||
if cc_version_major < 6:
|
||||
print_error(
|
||||
"Detected Clang version older than 6, which does not fully support "
|
||||
"C++17. Supported versions are Clang 6 and later."
|
||||
)
|
||||
Exit(255)
|
||||
if env["debug_paths_relative"] and cc_version_major < 10:
|
||||
elif env["debug_paths_relative"] and cc_version_major < 10:
|
||||
print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
|
||||
env["debug_paths_relative"] = False
|
||||
|
||||
elif env.msvc:
|
||||
# Ensure latest minor builds of Visual Studio 2017/2019.
|
||||
# https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
|
||||
@ -753,7 +752,7 @@ else:
|
||||
project_path = Dir("#").abspath
|
||||
env.Append(CCFLAGS=[f"-ffile-prefix-map={project_path}=."])
|
||||
else:
|
||||
if methods.using_clang(env) and not methods.is_vanilla_clang(env):
|
||||
if methods.is_apple_clang(env):
|
||||
# Apple Clang, its linker doesn't like -s.
|
||||
env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
|
||||
else:
|
||||
@ -1039,11 +1038,6 @@ GLSL_BUILDERS = {
|
||||
}
|
||||
env.Append(BUILDERS=GLSL_BUILDERS)
|
||||
|
||||
scons_cache_path = os.environ.get("SCONS_CACHE")
|
||||
if scons_cache_path is not None:
|
||||
CacheDir(scons_cache_path)
|
||||
print("Scons cache enabled... (path: '" + scons_cache_path + "')")
|
||||
|
||||
if env["compiledb"]:
|
||||
env.Tool("compilation_db")
|
||||
env.Alias("compiledb", env.CompilationDatabase())
|
||||
@ -1056,7 +1050,7 @@ if env["ninja"]:
|
||||
SetOption("experimental", "ninja")
|
||||
env["NINJA_FILE_NAME"] = env["ninja_file"]
|
||||
env["NINJA_DISABLE_AUTO_RUN"] = not env["ninja_auto_run"]
|
||||
env.Tool("ninja", "build.ninja")
|
||||
env.Tool("ninja", env["ninja_file"])
|
||||
|
||||
# Threads
|
||||
if env["threads"]:
|
||||
@ -1118,7 +1112,7 @@ atexit.register(print_elapsed_time)
|
||||
|
||||
|
||||
def purge_flaky_files():
|
||||
paths_to_keep = ["build.ninja"]
|
||||
paths_to_keep = [env["ninja_file"]]
|
||||
for build_failure in GetBuildFailures():
|
||||
path = build_failure.node.path
|
||||
if os.path.isfile(path) and path not in paths_to_keep:
|
||||
@ -1126,5 +1120,3 @@ def purge_flaky_files():
|
||||
|
||||
|
||||
atexit.register(purge_flaky_files)
|
||||
|
||||
methods.clean_cache(env)
|
||||
|
@ -194,7 +194,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
|
||||
|
||||
return cwd.replace_first(res_path, "res://");
|
||||
} else {
|
||||
int sep = path.rfind("/");
|
||||
int sep = path.rfind_char('/');
|
||||
if (sep == -1) {
|
||||
return "res://" + path;
|
||||
}
|
||||
@ -262,6 +262,12 @@ String ProjectSettings::globalize_path(const String &p_path) const {
|
||||
return p_path.replace("res:/", resource_path);
|
||||
}
|
||||
return p_path.replace("res://", "");
|
||||
} else if (p_path.begins_with("uid://")) {
|
||||
const String path = ResourceUID::uid_to_path(p_path);
|
||||
if (!resource_path.is_empty()) {
|
||||
return path.replace("res:/", resource_path);
|
||||
}
|
||||
return path.replace("res://", "");
|
||||
} else if (p_path.begins_with("user://")) {
|
||||
String data_dir = OS::get_singleton()->get_user_data_dir();
|
||||
if (!data_dir.is_empty()) {
|
||||
@ -300,7 +306,7 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
|
||||
}
|
||||
|
||||
{ // Feature overrides.
|
||||
int dot = p_name.operator String().find(".");
|
||||
int dot = p_name.operator String().find_char('.');
|
||||
if (dot != -1) {
|
||||
Vector<String> s = p_name.operator String().split(".");
|
||||
|
||||
@ -435,7 +441,7 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
|
||||
for (const _VCSort &E : vclist) {
|
||||
String prop_info_name = E.name;
|
||||
int dot = prop_info_name.find(".");
|
||||
int dot = prop_info_name.find_char('.');
|
||||
if (dot != -1 && !custom_prop_info.has(prop_info_name)) {
|
||||
prop_info_name = prop_info_name.substr(0, dot);
|
||||
}
|
||||
@ -508,16 +514,19 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p_from_version <= 5) {
|
||||
// Converts the device in events from -1 (emulated events) to -3 (all events).
|
||||
if (p_from_version == 5) {
|
||||
// Converts the device in events from -3 to -1.
|
||||
// -3 was introduced in GH-97707 as a way to prevent a clash in device IDs, but as reported in GH-99243, this leads to problems.
|
||||
// -3 was used during dev-releases, so this conversion helps to revert such affected projects.
|
||||
// This conversion doesn't affect any other projects, since -3 is not used otherwise.
|
||||
for (KeyValue<StringName, ProjectSettings::VariantContainer> &E : props) {
|
||||
if (String(E.key).begins_with("input/")) {
|
||||
Dictionary action = E.value.variant;
|
||||
Array events = action["events"];
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
Ref<InputEvent> ev = events[i];
|
||||
if (ev.is_valid() && ev->get_device() == -1) { // -1 was the previous value (GH-97707).
|
||||
ev->set_device(InputEvent::DEVICE_ID_ALL_DEVICES);
|
||||
if (ev.is_valid() && ev->get_device() == -3) {
|
||||
ev->set_device(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1092,7 +1101,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
|
||||
String category = E.name;
|
||||
String name = E.name;
|
||||
|
||||
int div = category.find("/");
|
||||
int div = category.find_char('/');
|
||||
|
||||
if (div < 0) {
|
||||
category = "";
|
||||
|
@ -163,7 +163,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, co
|
||||
|
||||
for (int i = 0; i < p_breakpoints.size(); i++) {
|
||||
const String &bp = p_breakpoints[i];
|
||||
int sp = bp.rfind(":");
|
||||
int sp = bp.rfind_char(':');
|
||||
ERR_CONTINUE_MSG(sp == -1, vformat("Invalid breakpoint: '%s', expected file:line format.", bp));
|
||||
|
||||
singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).to_int(), bp.substr(0, sp));
|
||||
|
@ -171,7 +171,7 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
|
||||
|
||||
} else {
|
||||
String key_value = line.get_slicec(' ', 1);
|
||||
int value_pos = key_value.find("=");
|
||||
int value_pos = key_value.find_char('=');
|
||||
|
||||
if (value_pos < 0) {
|
||||
print_line("Error: Invalid set format. Use: set key=value");
|
||||
@ -344,7 +344,7 @@ Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) {
|
||||
String breakpoint_part = p_line.get_slicec(' ', 1);
|
||||
Pair<String, int> breakpoint;
|
||||
|
||||
int last_colon = breakpoint_part.rfind(":");
|
||||
int last_colon = breakpoint_part.rfind_char(':');
|
||||
if (last_colon < 0) {
|
||||
print_line("Error: Invalid breakpoint format. Expected [source:line]");
|
||||
return breakpoint;
|
||||
|
@ -338,7 +338,7 @@ void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_va
|
||||
}
|
||||
|
||||
Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) {
|
||||
const int idx = p_msg.find(":");
|
||||
const int idx = p_msg.find_char(':');
|
||||
r_captured = false;
|
||||
if (idx < 0) { // No prefix, unknown message.
|
||||
return OK;
|
||||
@ -610,7 +610,7 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
|
||||
ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY);
|
||||
|
||||
const String cmd = arr[0];
|
||||
const int idx = cmd.find(":");
|
||||
const int idx = cmd.find_char(':');
|
||||
bool parsed = false;
|
||||
if (idx < 0) { // Not prefix, use scripts capture.
|
||||
capture_parse("core", cmd, arr[1], parsed);
|
||||
|
@ -224,7 +224,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
|
||||
uint16_t debug_port = 6007;
|
||||
|
||||
if (debug_host.contains(":")) {
|
||||
int sep_pos = debug_host.rfind(":");
|
||||
int sep_pos = debug_host.rfind_char(':');
|
||||
debug_port = debug_host.substr(sep_pos + 1).to_int();
|
||||
debug_host = debug_host.substr(0, sep_pos);
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool is_vararg() const override {
|
||||
return false;
|
||||
return vararg;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -236,7 +236,7 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
|
||||
String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
|
||||
r_options->push_back(name.quote());
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,9 @@
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
const int InputEvent::DEVICE_ID_EMULATION = -1;
|
||||
const int InputEvent::DEVICE_ID_INTERNAL = -2;
|
||||
|
||||
void InputEvent::set_device(int p_device) {
|
||||
device = p_device;
|
||||
emit_changed();
|
||||
|
@ -62,9 +62,8 @@ protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
inline static constexpr int DEVICE_ID_EMULATION = -1;
|
||||
inline static constexpr int DEVICE_ID_INTERNAL = -2;
|
||||
inline static constexpr int DEVICE_ID_ALL_DEVICES = -3; // Signify that a given Action can be triggered by any device.
|
||||
static const int DEVICE_ID_EMULATION;
|
||||
static const int DEVICE_ID_INTERNAL;
|
||||
|
||||
void set_device(int p_device);
|
||||
int get_device() const;
|
||||
|
@ -39,6 +39,8 @@
|
||||
|
||||
InputMap *InputMap::singleton = nullptr;
|
||||
|
||||
int InputMap::ALL_DEVICES = -1;
|
||||
|
||||
void InputMap::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
|
||||
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
|
||||
@ -104,7 +106,7 @@ void InputMap::get_argument_options(const StringName &p_function, int p_idx, Lis
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
|
||||
String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
|
||||
r_options->push_back(name.quote());
|
||||
}
|
||||
}
|
||||
@ -161,7 +163,7 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re
|
||||
int i = 0;
|
||||
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
|
||||
int device = E->get()->get_device();
|
||||
if (device == InputEvent::DEVICE_ID_ALL_DEVICES || device == p_event->get_device()) {
|
||||
if (device == ALL_DEVICES || device == p_event->get_device()) {
|
||||
if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
|
||||
if (r_event_index) {
|
||||
*r_event_index = i;
|
||||
@ -302,7 +304,7 @@ void InputMap::load_from_project_settings() {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
|
||||
String name = pi.name.substr(pi.name.find_char('/') + 1, pi.name.length());
|
||||
|
||||
Dictionary action = GLOBAL_GET(pi.name);
|
||||
float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE;
|
||||
|
@ -43,6 +43,11 @@ class InputMap : public Object {
|
||||
GDCLASS(InputMap, Object);
|
||||
|
||||
public:
|
||||
/**
|
||||
* A special value used to signify that a given Action can be triggered by any device
|
||||
*/
|
||||
static int ALL_DEVICES;
|
||||
|
||||
struct Action {
|
||||
int id;
|
||||
float deadzone;
|
||||
|
@ -155,9 +155,9 @@ Error DirAccess::make_dir_recursive(const String &p_dir) {
|
||||
} else if (full_dir.begins_with("user://")) {
|
||||
base = "user://";
|
||||
} else if (full_dir.is_network_share_path()) {
|
||||
int pos = full_dir.find("/", 2);
|
||||
int pos = full_dir.find_char('/', 2);
|
||||
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
|
||||
pos = full_dir.find("/", pos + 1);
|
||||
pos = full_dir.find_char('/', pos + 1);
|
||||
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
|
||||
base = full_dir.substr(0, pos + 1);
|
||||
} else if (full_dir.begins_with("/")) {
|
||||
|
@ -71,7 +71,7 @@ void FileAccess::_set_access_type(AccessType p_access) {
|
||||
|
||||
Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
|
||||
Ref<FileAccess> ret;
|
||||
if (p_path.begins_with("res://")) {
|
||||
if (p_path.begins_with("res://") || p_path.begins_with("uid://")) {
|
||||
ret = create(ACCESS_RESOURCES);
|
||||
} else if (p_path.begins_with("user://")) {
|
||||
ret = create(ACCESS_USERDATA);
|
||||
@ -183,13 +183,17 @@ FileAccess::AccessType FileAccess::get_access_type() const {
|
||||
}
|
||||
|
||||
String FileAccess::fix_path(const String &p_path) const {
|
||||
//helper used by file accesses that use a single filesystem
|
||||
// Helper used by file accesses that use a single filesystem.
|
||||
|
||||
String r_path = p_path.replace("\\", "/");
|
||||
|
||||
switch (_access_type) {
|
||||
case ACCESS_RESOURCES: {
|
||||
if (ProjectSettings::get_singleton()) {
|
||||
if (r_path.begins_with("uid://")) {
|
||||
r_path = ResourceUID::uid_to_path(r_path);
|
||||
}
|
||||
|
||||
if (r_path.begins_with("res://")) {
|
||||
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
|
||||
if (!resource_path.is_empty()) {
|
||||
|
@ -590,8 +590,6 @@ String DirAccessPack::get_current_dir(bool p_include_drive) const {
|
||||
}
|
||||
|
||||
bool DirAccessPack::file_exists(String p_file) {
|
||||
p_file = fix_path(p_file);
|
||||
|
||||
PackedData::PackedDir *pd = _find_dir(p_file.get_base_dir());
|
||||
if (!pd) {
|
||||
return false;
|
||||
@ -600,8 +598,6 @@ bool DirAccessPack::file_exists(String p_file) {
|
||||
}
|
||||
|
||||
bool DirAccessPack::dir_exists(String p_dir) {
|
||||
p_dir = fix_path(p_dir);
|
||||
|
||||
return _find_dir(p_dir) != nullptr;
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ Error HTTPClient::verify_headers(const Vector<String> &p_headers) {
|
||||
for (int i = 0; i < p_headers.size(); i++) {
|
||||
String sanitized = p_headers[i].strip_edges();
|
||||
ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, vformat("Invalid HTTP header at index %d: empty.", i));
|
||||
ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER,
|
||||
ERR_FAIL_COND_V_MSG(sanitized.find_char(':') < 1, ERR_INVALID_PARAMETER,
|
||||
vformat("Invalid HTTP header at index %d: String must contain header-value pair, delimited by ':', but was: '%s'.", i, p_headers[i]));
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() {
|
||||
get_response_headers(&rh);
|
||||
Dictionary ret;
|
||||
for (const String &s : rh) {
|
||||
int sp = s.find(":");
|
||||
int sp = s.find_char(':');
|
||||
if (sp == -1) {
|
||||
continue;
|
||||
}
|
||||
|
@ -508,11 +508,11 @@ Error HTTPClientTCP::poll() {
|
||||
continue;
|
||||
}
|
||||
if (s.begins_with("content-length:")) {
|
||||
body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
|
||||
body_size = s.substr(s.find_char(':') + 1, s.length()).strip_edges().to_int();
|
||||
body_left = body_size;
|
||||
|
||||
} else if (s.begins_with("transfer-encoding:")) {
|
||||
String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
|
||||
String encoding = header.substr(header.find_char(':') + 1, header.length()).strip_edges();
|
||||
if (encoding == "chunked") {
|
||||
chunked = true;
|
||||
}
|
||||
|
@ -2617,7 +2617,7 @@ Error Image::load(const String &p_path) {
|
||||
WARN_PRINT(vformat("Loaded resource as image file, this will not work on export: '%s'. Instead, import the image file as an Image resource and load it normally as a resource.", path));
|
||||
}
|
||||
#endif
|
||||
return ImageLoader::load_image(ResourceUID::ensure_path(p_path), this);
|
||||
return ImageLoader::load_image(path, this);
|
||||
}
|
||||
|
||||
Ref<Image> Image::load_from_file(const String &p_path) {
|
||||
|
@ -82,15 +82,16 @@ void ImageFormatLoaderExtension::_bind_methods() {
|
||||
|
||||
Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
|
||||
ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "Can't load an image: invalid Image object.");
|
||||
const String file = ResourceUID::ensure_path(p_file);
|
||||
|
||||
Ref<FileAccess> f = p_custom;
|
||||
if (f.is_null()) {
|
||||
Error err;
|
||||
f = FileAccess::open(p_file, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), err, vformat("Error opening file '%s'.", p_file));
|
||||
f = FileAccess::open(file, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), err, vformat("Error opening file '%s'.", file));
|
||||
}
|
||||
|
||||
String extension = p_file.get_extension();
|
||||
String extension = file.get_extension();
|
||||
|
||||
for (int i = 0; i < loader.size(); i++) {
|
||||
if (!loader[i]->recognize(extension)) {
|
||||
@ -98,7 +99,7 @@ Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<File
|
||||
}
|
||||
Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale);
|
||||
if (err != OK) {
|
||||
ERR_PRINT(vformat("Error loading image: '%s'.", p_file));
|
||||
ERR_PRINT(vformat("Error loading image: '%s'.", file));
|
||||
}
|
||||
|
||||
if (err != ERR_FILE_UNRECOGNIZED) {
|
||||
|
@ -661,12 +661,12 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
||||
List<Ref<PListNode>> stack;
|
||||
String key;
|
||||
while (pos >= 0) {
|
||||
int open_token_s = p_string.find("<", pos);
|
||||
int open_token_s = p_string.find_char('<', pos);
|
||||
if (open_token_s == -1) {
|
||||
r_err_out = "Unexpected end of data. No tags found.";
|
||||
return false;
|
||||
}
|
||||
int open_token_e = p_string.find(">", open_token_s);
|
||||
int open_token_e = p_string.find_char('>', open_token_s);
|
||||
pos = open_token_e;
|
||||
|
||||
String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
|
||||
@ -676,7 +676,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
||||
}
|
||||
String value;
|
||||
if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
|
||||
int end_token_e = p_string.find(">", open_token_s);
|
||||
int end_token_e = p_string.find_char('>', open_token_s);
|
||||
pos = end_token_e;
|
||||
continue;
|
||||
}
|
||||
@ -769,7 +769,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
||||
r_err_out = vformat("Mismatched <%s> tag.", token);
|
||||
return false;
|
||||
}
|
||||
int end_token_e = p_string.find(">", end_token_s);
|
||||
int end_token_e = p_string.find_char('>', end_token_s);
|
||||
pos = end_token_e;
|
||||
String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
|
||||
if (end_token != token) {
|
||||
|
@ -295,13 +295,13 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
|
||||
load_paths_stack.push_back(original_path);
|
||||
|
||||
// Try all loaders and pick the first match for the type hint
|
||||
bool loader_found = false;
|
||||
bool found = false;
|
||||
Ref<Resource> res;
|
||||
for (int i = 0; i < loader_count; i++) {
|
||||
if (!loader[i]->recognize_path(p_path, p_type_hint)) {
|
||||
continue;
|
||||
}
|
||||
loader_found = true;
|
||||
found = true;
|
||||
res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
|
||||
if (!res.is_null()) {
|
||||
break;
|
||||
@ -316,24 +316,15 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!loader_found) {
|
||||
if (r_error) {
|
||||
*r_error = ERR_FILE_UNRECOGNIZED;
|
||||
}
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint));
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(found, Ref<Resource>(),
|
||||
vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path));
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES);
|
||||
if (!file_check->file_exists(p_path)) {
|
||||
if (r_error) {
|
||||
*r_error = ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint));
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint));
|
||||
#endif
|
||||
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path));
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint));
|
||||
}
|
||||
|
||||
// This implementation must allow re-entrancy for a task that started awaiting in a deeper stack frame.
|
||||
@ -1206,7 +1197,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
|
||||
|
||||
int best_score = 0;
|
||||
for (int i = 0; i < res_remaps.size(); i++) {
|
||||
int split = res_remaps[i].rfind(":");
|
||||
int split = res_remaps[i].rfind_char(':');
|
||||
if (split == -1) {
|
||||
continue;
|
||||
}
|
||||
@ -1498,11 +1489,11 @@ Vector<String> ResourceLoader::list_directory(const String &p_directory) {
|
||||
}
|
||||
} else {
|
||||
if (d.ends_with(".import") || d.ends_with(".remap") || d.ends_with(".uid")) {
|
||||
d = d.substr(0, d.rfind("."));
|
||||
d = d.substr(0, d.rfind_char('.'));
|
||||
}
|
||||
|
||||
if (d.ends_with(".gdc")) {
|
||||
d = d.substr(0, d.rfind("."));
|
||||
d = d.substr(0, d.rfind_char('.'));
|
||||
d += ".gd";
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
||||
// Record plural rule.
|
||||
int p_start = config.find("Plural-Forms");
|
||||
if (p_start != -1) {
|
||||
int p_end = config.find("\n", p_start);
|
||||
int p_end = config.find_char('\n', p_start);
|
||||
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
|
||||
}
|
||||
} else {
|
||||
@ -224,7 +224,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
||||
// Record plural rule.
|
||||
int p_start = config.find("Plural-Forms");
|
||||
if (p_start != -1) {
|
||||
int p_end = config.find("\n", p_start);
|
||||
int p_end = config.find_char('\n', p_start);
|
||||
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
|
||||
plural_forms = translation->get_plural_forms();
|
||||
}
|
||||
@ -324,7 +324,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
||||
Vector<String> configs = config.split("\n");
|
||||
for (int i = 0; i < configs.size(); i++) {
|
||||
String c = configs[i].strip_edges();
|
||||
int p = c.find(":");
|
||||
int p = c.find_char(':');
|
||||
if (p == -1) {
|
||||
continue;
|
||||
}
|
||||
|
@ -596,101 +596,229 @@ Projection Projection::inverse() const {
|
||||
}
|
||||
|
||||
void Projection::invert() {
|
||||
int i, j, k;
|
||||
int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */
|
||||
real_t pvt_val; /* Value of current pivot element */
|
||||
real_t hold; /* Temporary storage */
|
||||
real_t determinant = 1.0f;
|
||||
for (k = 0; k < 4; k++) {
|
||||
/** Locate k'th pivot element **/
|
||||
pvt_val = columns[k][k]; /** Initialize for search **/
|
||||
pvt_i[k] = k;
|
||||
pvt_j[k] = k;
|
||||
for (i = k; i < 4; i++) {
|
||||
for (j = k; j < 4; j++) {
|
||||
if (Math::abs(columns[i][j]) > Math::abs(pvt_val)) {
|
||||
pvt_i[k] = i;
|
||||
pvt_j[k] = j;
|
||||
pvt_val = columns[i][j];
|
||||
// Adapted from Mesa's `src/util/u_math.c` `util_invert_mat4x4`.
|
||||
// MIT licensed. Copyright 2008 VMware, Inc. Authored by Jacques Leroy.
|
||||
Projection temp;
|
||||
real_t *out = (real_t *)temp.columns;
|
||||
real_t *m = (real_t *)columns;
|
||||
|
||||
real_t wtmp[4][8];
|
||||
real_t m0, m1, m2, m3, s;
|
||||
real_t *r0, *r1, *r2, *r3;
|
||||
|
||||
#define MAT(m, r, c) (m)[(c) * 4 + (r)]
|
||||
|
||||
r0 = wtmp[0];
|
||||
r1 = wtmp[1];
|
||||
r2 = wtmp[2];
|
||||
r3 = wtmp[3];
|
||||
|
||||
r0[0] = MAT(m, 0, 0);
|
||||
r0[1] = MAT(m, 0, 1);
|
||||
r0[2] = MAT(m, 0, 2);
|
||||
r0[3] = MAT(m, 0, 3);
|
||||
r0[4] = 1.0;
|
||||
r0[5] = 0.0;
|
||||
r0[6] = 0.0;
|
||||
r0[7] = 0.0;
|
||||
|
||||
r1[0] = MAT(m, 1, 0);
|
||||
r1[1] = MAT(m, 1, 1);
|
||||
r1[2] = MAT(m, 1, 2);
|
||||
r1[3] = MAT(m, 1, 3);
|
||||
r1[5] = 1.0;
|
||||
r1[4] = 0.0;
|
||||
r1[6] = 0.0;
|
||||
r1[7] = 0.0;
|
||||
|
||||
r2[0] = MAT(m, 2, 0);
|
||||
r2[1] = MAT(m, 2, 1);
|
||||
r2[2] = MAT(m, 2, 2);
|
||||
r2[3] = MAT(m, 2, 3);
|
||||
r2[6] = 1.0;
|
||||
r2[4] = 0.0;
|
||||
r2[5] = 0.0;
|
||||
r2[7] = 0.0;
|
||||
|
||||
r3[0] = MAT(m, 3, 0);
|
||||
r3[1] = MAT(m, 3, 1);
|
||||
r3[2] = MAT(m, 3, 2);
|
||||
r3[3] = MAT(m, 3, 3);
|
||||
|
||||
r3[7] = 1.0;
|
||||
r3[4] = 0.0;
|
||||
r3[5] = 0.0;
|
||||
r3[6] = 0.0;
|
||||
|
||||
/* choose pivot - or die */
|
||||
if (Math::abs(r3[0]) > Math::abs(r2[0])) {
|
||||
SWAP(r3, r2);
|
||||
}
|
||||
if (Math::abs(r2[0]) > Math::abs(r1[0])) {
|
||||
SWAP(r2, r1);
|
||||
}
|
||||
if (Math::abs(r1[0]) > Math::abs(r0[0])) {
|
||||
SWAP(r1, r0);
|
||||
}
|
||||
ERR_FAIL_COND(0.0 == r0[0]);
|
||||
|
||||
/* eliminate first variable */
|
||||
m1 = r1[0] / r0[0];
|
||||
m2 = r2[0] / r0[0];
|
||||
m3 = r3[0] / r0[0];
|
||||
s = r0[1];
|
||||
r1[1] -= m1 * s;
|
||||
r2[1] -= m2 * s;
|
||||
r3[1] -= m3 * s;
|
||||
s = r0[2];
|
||||
r1[2] -= m1 * s;
|
||||
r2[2] -= m2 * s;
|
||||
r3[2] -= m3 * s;
|
||||
s = r0[3];
|
||||
r1[3] -= m1 * s;
|
||||
r2[3] -= m2 * s;
|
||||
r3[3] -= m3 * s;
|
||||
s = r0[4];
|
||||
if (s != 0.0) {
|
||||
r1[4] -= m1 * s;
|
||||
r2[4] -= m2 * s;
|
||||
r3[4] -= m3 * s;
|
||||
}
|
||||
s = r0[5];
|
||||
if (s != 0.0) {
|
||||
r1[5] -= m1 * s;
|
||||
r2[5] -= m2 * s;
|
||||
r3[5] -= m3 * s;
|
||||
}
|
||||
s = r0[6];
|
||||
if (s != 0.0) {
|
||||
r1[6] -= m1 * s;
|
||||
r2[6] -= m2 * s;
|
||||
r3[6] -= m3 * s;
|
||||
}
|
||||
s = r0[7];
|
||||
if (s != 0.0) {
|
||||
r1[7] -= m1 * s;
|
||||
r2[7] -= m2 * s;
|
||||
r3[7] -= m3 * s;
|
||||
}
|
||||
|
||||
/** Product of pivots, gives determinant when finished **/
|
||||
determinant *= pvt_val;
|
||||
if (Math::is_zero_approx(determinant)) {
|
||||
return; /** Matrix is singular (zero determinant). **/
|
||||
/* choose pivot - or die */
|
||||
if (Math::abs(r3[1]) > Math::abs(r2[1])) {
|
||||
SWAP(r3, r2);
|
||||
}
|
||||
if (Math::abs(r2[1]) > Math::abs(r1[1])) {
|
||||
SWAP(r2, r1);
|
||||
}
|
||||
ERR_FAIL_COND(0.0 == r1[1]);
|
||||
|
||||
/* eliminate second variable */
|
||||
m2 = r2[1] / r1[1];
|
||||
m3 = r3[1] / r1[1];
|
||||
r2[2] -= m2 * r1[2];
|
||||
r3[2] -= m3 * r1[2];
|
||||
r2[3] -= m2 * r1[3];
|
||||
r3[3] -= m3 * r1[3];
|
||||
s = r1[4];
|
||||
if (0.0 != s) {
|
||||
r2[4] -= m2 * s;
|
||||
r3[4] -= m3 * s;
|
||||
}
|
||||
s = r1[5];
|
||||
if (0.0 != s) {
|
||||
r2[5] -= m2 * s;
|
||||
r3[5] -= m3 * s;
|
||||
}
|
||||
s = r1[6];
|
||||
if (0.0 != s) {
|
||||
r2[6] -= m2 * s;
|
||||
r3[6] -= m3 * s;
|
||||
}
|
||||
s = r1[7];
|
||||
if (0.0 != s) {
|
||||
r2[7] -= m2 * s;
|
||||
r3[7] -= m3 * s;
|
||||
}
|
||||
|
||||
/** "Interchange" rows (with sign change stuff) **/
|
||||
i = pvt_i[k];
|
||||
if (i != k) { /** If rows are different **/
|
||||
for (j = 0; j < 4; j++) {
|
||||
hold = -columns[k][j];
|
||||
columns[k][j] = columns[i][j];
|
||||
columns[i][j] = hold;
|
||||
}
|
||||
/* choose pivot - or die */
|
||||
if (Math::abs(r3[2]) > Math::abs(r2[2])) {
|
||||
SWAP(r3, r2);
|
||||
}
|
||||
ERR_FAIL_COND(0.0 == r2[2]);
|
||||
|
||||
/** "Interchange" columns **/
|
||||
j = pvt_j[k];
|
||||
if (j != k) { /** If columns are different **/
|
||||
for (i = 0; i < 4; i++) {
|
||||
hold = -columns[i][k];
|
||||
columns[i][k] = columns[i][j];
|
||||
columns[i][j] = hold;
|
||||
}
|
||||
}
|
||||
/* eliminate third variable */
|
||||
m3 = r3[2] / r2[2];
|
||||
r3[3] -= m3 * r2[3];
|
||||
r3[4] -= m3 * r2[4];
|
||||
r3[5] -= m3 * r2[5];
|
||||
r3[6] -= m3 * r2[6];
|
||||
r3[7] -= m3 * r2[7];
|
||||
|
||||
/** Divide column by minus pivot value **/
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i != k) {
|
||||
columns[i][k] /= (-pvt_val);
|
||||
}
|
||||
}
|
||||
/* last check */
|
||||
ERR_FAIL_COND(0.0 == r3[3]);
|
||||
|
||||
/** Reduce the matrix **/
|
||||
for (i = 0; i < 4; i++) {
|
||||
hold = columns[i][k];
|
||||
for (j = 0; j < 4; j++) {
|
||||
if (i != k && j != k) {
|
||||
columns[i][j] += hold * columns[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
s = 1.0 / r3[3]; /* now back substitute row 3 */
|
||||
r3[4] *= s;
|
||||
r3[5] *= s;
|
||||
r3[6] *= s;
|
||||
r3[7] *= s;
|
||||
|
||||
/** Divide row by pivot **/
|
||||
for (j = 0; j < 4; j++) {
|
||||
if (j != k) {
|
||||
columns[k][j] /= pvt_val;
|
||||
}
|
||||
}
|
||||
m2 = r2[3]; /* now back substitute row 2 */
|
||||
s = 1.0 / r2[2];
|
||||
r2[4] = s * (r2[4] - r3[4] * m2);
|
||||
r2[5] = s * (r2[5] - r3[5] * m2);
|
||||
r2[6] = s * (r2[6] - r3[6] * m2);
|
||||
r2[7] = s * (r2[7] - r3[7] * m2);
|
||||
m1 = r1[3];
|
||||
r1[4] -= r3[4] * m1;
|
||||
r1[5] -= r3[5] * m1;
|
||||
r1[6] -= r3[6] * m1;
|
||||
r1[7] -= r3[7] * m1;
|
||||
m0 = r0[3];
|
||||
r0[4] -= r3[4] * m0;
|
||||
r0[5] -= r3[5] * m0;
|
||||
r0[6] -= r3[6] * m0;
|
||||
r0[7] -= r3[7] * m0;
|
||||
|
||||
/** Replace pivot by reciprocal (at last we can touch it). **/
|
||||
columns[k][k] = 1.0 / pvt_val;
|
||||
}
|
||||
m1 = r1[2]; /* now back substitute row 1 */
|
||||
s = 1.0 / r1[1];
|
||||
r1[4] = s * (r1[4] - r2[4] * m1);
|
||||
r1[5] = s * (r1[5] - r2[5] * m1),
|
||||
r1[6] = s * (r1[6] - r2[6] * m1);
|
||||
r1[7] = s * (r1[7] - r2[7] * m1);
|
||||
m0 = r0[2];
|
||||
r0[4] -= r2[4] * m0;
|
||||
r0[5] -= r2[5] * m0;
|
||||
r0[6] -= r2[6] * m0;
|
||||
r0[7] -= r2[7] * m0;
|
||||
|
||||
/* That was most of the work, one final pass of row/column interchange */
|
||||
/* to finish */
|
||||
for (k = 4 - 2; k >= 0; k--) { /* Don't need to work with 1 by 1 corner*/
|
||||
i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */
|
||||
if (i != k) { /* If rows are different */
|
||||
for (j = 0; j < 4; j++) {
|
||||
hold = columns[k][j];
|
||||
columns[k][j] = -columns[i][j];
|
||||
columns[i][j] = hold;
|
||||
}
|
||||
}
|
||||
m0 = r0[1]; /* now back substitute row 0 */
|
||||
s = 1.0 / r0[0];
|
||||
r0[4] = s * (r0[4] - r1[4] * m0);
|
||||
r0[5] = s * (r0[5] - r1[5] * m0),
|
||||
r0[6] = s * (r0[6] - r1[6] * m0);
|
||||
r0[7] = s * (r0[7] - r1[7] * m0);
|
||||
|
||||
j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */
|
||||
if (j != k) { /* If columns are different */
|
||||
for (i = 0; i < 4; i++) {
|
||||
hold = columns[i][k];
|
||||
columns[i][k] = -columns[i][j];
|
||||
columns[i][j] = hold;
|
||||
}
|
||||
}
|
||||
}
|
||||
MAT(out, 0, 0) = r0[4];
|
||||
MAT(out, 0, 1) = r0[5];
|
||||
MAT(out, 0, 2) = r0[6];
|
||||
MAT(out, 0, 3) = r0[7];
|
||||
MAT(out, 1, 0) = r1[4];
|
||||
MAT(out, 1, 1) = r1[5];
|
||||
MAT(out, 1, 2) = r1[6];
|
||||
MAT(out, 1, 3) = r1[7];
|
||||
MAT(out, 2, 0) = r2[4];
|
||||
MAT(out, 2, 1) = r2[5];
|
||||
MAT(out, 2, 2) = r2[6];
|
||||
MAT(out, 2, 3) = r2[7];
|
||||
MAT(out, 3, 0) = r3[4];
|
||||
MAT(out, 3, 1) = r3[5];
|
||||
MAT(out, 3, 2) = r3[6];
|
||||
MAT(out, 3, 3) = r3[7];
|
||||
|
||||
#undef MAT
|
||||
|
||||
*this = temp;
|
||||
}
|
||||
|
||||
void Projection::flip_y() {
|
||||
|
@ -407,7 +407,7 @@ NodePath::NodePath(const String &p_path) {
|
||||
bool absolute = (path[0] == '/');
|
||||
bool last_is_slash = true;
|
||||
int slices = 0;
|
||||
int subpath_pos = path.find(":");
|
||||
int subpath_pos = path.find_char(':');
|
||||
|
||||
if (subpath_pos != -1) {
|
||||
int from = subpath_pos + 1;
|
||||
|
@ -227,11 +227,11 @@ void TranslationPO::set_plural_rule(const String &p_plural_rule) {
|
||||
// Set plural_forms and plural_rule.
|
||||
// p_plural_rule passed in has the form "Plural-Forms: nplurals=2; plural=(n >= 2);".
|
||||
|
||||
int first_semi_col = p_plural_rule.find(";");
|
||||
plural_forms = p_plural_rule.substr(p_plural_rule.find("=") + 1, first_semi_col - (p_plural_rule.find("=") + 1)).to_int();
|
||||
int first_semi_col = p_plural_rule.find_char(';');
|
||||
plural_forms = p_plural_rule.substr(p_plural_rule.find_char('=') + 1, first_semi_col - (p_plural_rule.find_char('=') + 1)).to_int();
|
||||
|
||||
int expression_start = p_plural_rule.find("=", first_semi_col) + 1;
|
||||
int second_semi_col = p_plural_rule.rfind(";");
|
||||
int expression_start = p_plural_rule.find_char('=', first_semi_col) + 1;
|
||||
int second_semi_col = p_plural_rule.rfind_char(';');
|
||||
plural_rule = p_plural_rule.substr(expression_start, second_semi_col - expression_start).strip_edges();
|
||||
|
||||
// Setup the cache to make evaluating plural rule faster later on.
|
||||
|
@ -246,27 +246,27 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r
|
||||
base = base.substr(pos + 3, base.length() - pos - 3);
|
||||
}
|
||||
}
|
||||
pos = base.find("#");
|
||||
pos = base.find_char('#');
|
||||
// Fragment
|
||||
if (pos != -1) {
|
||||
r_fragment = base.substr(pos + 1);
|
||||
base = base.substr(0, pos);
|
||||
}
|
||||
pos = base.find("/");
|
||||
pos = base.find_char('/');
|
||||
// Path
|
||||
if (pos != -1) {
|
||||
r_path = base.substr(pos, base.length() - pos);
|
||||
base = base.substr(0, pos);
|
||||
}
|
||||
// Host
|
||||
pos = base.find("@");
|
||||
pos = base.find_char('@');
|
||||
if (pos != -1) {
|
||||
// Strip credentials
|
||||
base = base.substr(pos + 1, base.length() - pos - 1);
|
||||
}
|
||||
if (base.begins_with("[")) {
|
||||
// Literal IPv6
|
||||
pos = base.rfind("]");
|
||||
pos = base.rfind_char(']');
|
||||
if (pos == -1) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
@ -277,7 +277,7 @@ Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r
|
||||
if (base.get_slice_count(":") > 2) {
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
pos = base.rfind(":");
|
||||
pos = base.rfind_char(':');
|
||||
if (pos == -1) {
|
||||
r_host = base;
|
||||
base = "";
|
||||
@ -2641,7 +2641,7 @@ int64_t String::to_int() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int to = (find(".") >= 0) ? find(".") : length();
|
||||
int to = (find_char('.') >= 0) ? find_char('.') : length();
|
||||
|
||||
int64_t integer = 0;
|
||||
int64_t sign = 1;
|
||||
@ -4580,7 +4580,7 @@ String String::simplify_path() const {
|
||||
if (p == -1) {
|
||||
p = s.find(":\\");
|
||||
}
|
||||
if (p != -1 && p < s.find("/")) {
|
||||
if (p != -1 && p < s.find_char('/')) {
|
||||
drive = s.substr(0, p + 2);
|
||||
s = s.substr(p + 2);
|
||||
}
|
||||
@ -5025,7 +5025,7 @@ String String::xml_unescape() const {
|
||||
|
||||
String String::pad_decimals(int p_digits) const {
|
||||
String s = *this;
|
||||
int c = s.find(".");
|
||||
int c = s.find_char('.');
|
||||
|
||||
if (c == -1) {
|
||||
if (p_digits <= 0) {
|
||||
@ -5049,7 +5049,7 @@ String String::pad_decimals(int p_digits) const {
|
||||
|
||||
String String::pad_zeros(int p_digits) const {
|
||||
String s = *this;
|
||||
int end = s.find(".");
|
||||
int end = s.find_char('.');
|
||||
|
||||
if (end == -1) {
|
||||
end = s.length();
|
||||
@ -5316,7 +5316,7 @@ String String::validate_filename() const {
|
||||
}
|
||||
|
||||
bool String::is_valid_ip_address() const {
|
||||
if (find(":") >= 0) {
|
||||
if (find_char(':') >= 0) {
|
||||
Vector<String> ip = split(":");
|
||||
for (int i = 0; i < ip.size(); i++) {
|
||||
const String &n = ip[i];
|
||||
@ -5386,13 +5386,13 @@ String String::get_base_dir() const {
|
||||
// Windows UNC network share path.
|
||||
if (end == 0) {
|
||||
if (is_network_share_path()) {
|
||||
basepos = find("/", 2);
|
||||
basepos = find_char('/', 2);
|
||||
if (basepos == -1) {
|
||||
basepos = find("\\", 2);
|
||||
basepos = find_char('\\', 2);
|
||||
}
|
||||
int servpos = find("/", basepos + 1);
|
||||
int servpos = find_char('/', basepos + 1);
|
||||
if (servpos == -1) {
|
||||
servpos = find("\\", basepos + 1);
|
||||
servpos = find_char('\\', basepos + 1);
|
||||
}
|
||||
if (servpos != -1) {
|
||||
end = servpos + 1;
|
||||
@ -5416,7 +5416,7 @@ String String::get_base_dir() const {
|
||||
rs = *this;
|
||||
}
|
||||
|
||||
int sep = MAX(rs.rfind("/"), rs.rfind("\\"));
|
||||
int sep = MAX(rs.rfind_char('/'), rs.rfind_char('\\'));
|
||||
if (sep == -1) {
|
||||
return base;
|
||||
}
|
||||
@ -5425,7 +5425,7 @@ String String::get_base_dir() const {
|
||||
}
|
||||
|
||||
String String::get_file() const {
|
||||
int sep = MAX(rfind("/"), rfind("\\"));
|
||||
int sep = MAX(rfind_char('/'), rfind_char('\\'));
|
||||
if (sep == -1) {
|
||||
return *this;
|
||||
}
|
||||
@ -5434,8 +5434,8 @@ String String::get_file() const {
|
||||
}
|
||||
|
||||
String String::get_extension() const {
|
||||
int pos = rfind(".");
|
||||
if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) {
|
||||
int pos = rfind_char('.');
|
||||
if (pos < 0 || pos < MAX(rfind_char('/'), rfind_char('\\'))) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -5533,8 +5533,8 @@ String String::validate_node_name() const {
|
||||
}
|
||||
|
||||
String String::get_basename() const {
|
||||
int pos = rfind(".");
|
||||
if (pos < 0 || pos < MAX(rfind("/"), rfind("\\"))) {
|
||||
int pos = rfind_char('.');
|
||||
if (pos < 0 || pos < MAX(rfind_char('/'), rfind_char('\\'))) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns if the given line is foldable, that is, it has indented lines right below it or a comment / string block.
|
||||
Returns [code]true[/code] if the given line is foldable. A line is foldable if it is the start of a valid code region (see [method get_code_region_start_tag]), if it is the start of a comment or string block, or if the next non-empty line is more indented (see [method TextEdit.get_indent_level]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="cancel_code_completion">
|
||||
@ -153,7 +153,7 @@
|
||||
<method name="do_indent">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Perform an indent as if the user activated the "ui_text_indent" action.
|
||||
If there is no selection, indentation is inserted at the caret. Otherwise, the selected lines are indented like [method indent_lines]. Equivalent to the [member ProjectSettings.input/ui_text_indent] action. The indentation characters used depend on [member indent_use_spaces] and [member indent_size].
|
||||
</description>
|
||||
</method>
|
||||
<method name="duplicate_lines">
|
||||
@ -276,7 +276,7 @@
|
||||
<method name="get_folded_lines" qualifiers="const">
|
||||
<return type="int[]" />
|
||||
<description>
|
||||
Returns all lines that are current folded.
|
||||
Returns all lines that are currently folded.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_text_for_code_completion" qualifiers="const">
|
||||
@ -330,7 +330,7 @@
|
||||
<method name="indent_lines">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Indents selected lines, or in the case of no selection the caret line by one.
|
||||
Indents all lines that are selected or have a caret on them. Uses spaces or a tab depending on [member indent_use_spaces]. See [method unindent_lines].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_in_comment" qualifiers="const">
|
||||
@ -353,42 +353,42 @@
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns whether the line at the specified index is bookmarked or not.
|
||||
Returns [code]true[/code] if the given line is bookmarked. See [method set_line_as_bookmarked].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_line_breakpointed" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns whether the line at the specified index is breakpointed or not.
|
||||
Returns [code]true[/code] if the given line is breakpointed. See [method set_line_as_breakpoint].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_line_code_region_end" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns whether the line at the specified index is a code region end.
|
||||
Returns [code]true[/code] if the given line is a code region end. See [method set_code_region_tags].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_line_code_region_start" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns whether the line at the specified index is a code region start.
|
||||
Returns [code]true[/code] if the given line is a code region start. See [method set_code_region_tags].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_line_executing" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns whether the line at the specified index is marked as executing or not.
|
||||
Returns [code]true[/code] if the given line is marked as executing. See [method set_line_as_executing].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_line_folded" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns whether the line at the specified index is folded or not.
|
||||
Returns [code]true[/code] if the given line is folded. See [method fold_line].
|
||||
</description>
|
||||
</method>
|
||||
<method name="move_lines_down">
|
||||
@ -442,7 +442,7 @@
|
||||
<return type="void" />
|
||||
<param index="0" name="draw_below" type="bool" />
|
||||
<description>
|
||||
Sets if the code hint should draw below the text.
|
||||
If [code]true[/code], the code hint will draw below the main caret. If [code]false[/code], the code hint will draw above the main caret. See [method set_code_hint].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_code_region_tags">
|
||||
@ -458,7 +458,7 @@
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="bookmarked" type="bool" />
|
||||
<description>
|
||||
Sets the line as bookmarked.
|
||||
Sets the given line as bookmarked. If [code]true[/code] and [member gutters_draw_bookmarks] is [code]true[/code], draws the [theme_item bookmark] icon in the gutter for this line. See [method get_bookmarked_lines] and [method is_line_bookmarked].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_line_as_breakpoint">
|
||||
@ -466,7 +466,7 @@
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="breakpointed" type="bool" />
|
||||
<description>
|
||||
Sets the line as breakpointed.
|
||||
Sets the given line as a breakpoint. If [code]true[/code] and [member gutters_draw_breakpoints_gutter] is [code]true[/code], draws the [theme_item breakpoint] icon in the gutter for this line. See [method get_breakpointed_lines] and [method is_line_breakpointed].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_line_as_executing">
|
||||
@ -474,7 +474,7 @@
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="executing" type="bool" />
|
||||
<description>
|
||||
Sets the line as executing.
|
||||
Sets the given line as executing. If [code]true[/code] and [member gutters_draw_executing_lines] is [code]true[/code], draws the [theme_item executing_line] icon in the gutter for this line. See [method get_executing_lines] and [method is_line_executing].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_symbol_lookup_word_as_valid">
|
||||
@ -500,20 +500,20 @@
|
||||
<method name="unfold_all_lines">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Unfolds all lines, folded or not.
|
||||
Unfolds all lines that are folded.
|
||||
</description>
|
||||
</method>
|
||||
<method name="unfold_line">
|
||||
<return type="void" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Unfolds all lines that were previously folded.
|
||||
Unfolds the given line if it is folded or if it is hidden under a folded line.
|
||||
</description>
|
||||
</method>
|
||||
<method name="unindent_lines">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Unindents selected lines, or in the case of no selection the caret line by one. Same as performing "ui_text_unindent" action.
|
||||
Unindents all lines that are selected or have a caret on them. Uses spaces or a tab depending on [member indent_use_spaces]. Equivalent to the [member ProjectSettings.input/ui_text_dedent] action. See [method indent_lines].
|
||||
</description>
|
||||
</method>
|
||||
<method name="update_code_completion_options">
|
||||
@ -527,16 +527,16 @@
|
||||
</methods>
|
||||
<members>
|
||||
<member name="auto_brace_completion_enabled" type="bool" setter="set_auto_brace_completion_enabled" getter="is_auto_brace_completion_enabled" default="false">
|
||||
Sets whether brace pairs should be autocompleted.
|
||||
If [code]true[/code], uses [member auto_brace_completion_pairs] to automatically insert the closing brace when the opening brace is inserted by typing or autocompletion. Also automatically removes the closing brace when using backspace on the opening brace.
|
||||
</member>
|
||||
<member name="auto_brace_completion_highlight_matching" type="bool" setter="set_highlight_matching_braces_enabled" getter="is_highlight_matching_braces_enabled" default="false">
|
||||
Highlight mismatching brace pairs.
|
||||
If [code]true[/code], highlights brace pairs when the caret is on either one, using [member auto_brace_completion_pairs]. If matching, the pairs will be underlined. If a brace is unmatched, it is colored with [theme_item brace_mismatch_color].
|
||||
</member>
|
||||
<member name="auto_brace_completion_pairs" type="Dictionary" setter="set_auto_brace_completion_pairs" getter="get_auto_brace_completion_pairs" default="{ "\"": "\"", "'": "'", "(": ")", "[": "]", "{": "}" }">
|
||||
Sets the brace pairs to be autocompleted.
|
||||
Sets the brace pairs to be autocompleted. For each entry in the dictionary, the key is the opening brace and the value is the closing brace that matches it. A brace is a [String] made of symbols. See [member auto_brace_completion_enabled] and [member auto_brace_completion_highlight_matching].
|
||||
</member>
|
||||
<member name="code_completion_enabled" type="bool" setter="set_code_completion_enabled" getter="is_code_completion_enabled" default="false">
|
||||
Sets whether code completion is allowed.
|
||||
If [code]true[/code], the [member ProjectSettings.input/ui_text_completion_query] action requests code completion. To handle it, see [method _request_code_completion] or [signal code_completion_requested].
|
||||
</member>
|
||||
<member name="code_completion_prefixes" type="String[]" setter="set_code_completion_prefixes" getter="get_code_completion_prefixes" default="[]">
|
||||
Sets prefixes that will trigger code completion.
|
||||
@ -548,28 +548,28 @@
|
||||
Sets the string delimiters. All existing string delimiters will be removed.
|
||||
</member>
|
||||
<member name="gutters_draw_bookmarks" type="bool" setter="set_draw_bookmarks_gutter" getter="is_drawing_bookmarks_gutter" default="false">
|
||||
Sets if bookmarked should be drawn in the gutter. This gutter is shared with breakpoints and executing lines.
|
||||
If [code]true[/code], bookmarks are drawn in the gutter. This gutter is shared with breakpoints and executing lines. See [method set_line_as_bookmarked].
|
||||
</member>
|
||||
<member name="gutters_draw_breakpoints_gutter" type="bool" setter="set_draw_breakpoints_gutter" getter="is_drawing_breakpoints_gutter" default="false">
|
||||
Sets if breakpoints should be drawn in the gutter. This gutter is shared with bookmarks and executing lines.
|
||||
If [code]true[/code], breakpoints are drawn in the gutter. This gutter is shared with bookmarks and executing lines. Clicking the gutter will toggle the breakpoint for the line, see [method set_line_as_breakpoint].
|
||||
</member>
|
||||
<member name="gutters_draw_executing_lines" type="bool" setter="set_draw_executing_lines_gutter" getter="is_drawing_executing_lines_gutter" default="false">
|
||||
Sets if executing lines should be marked in the gutter. This gutter is shared with breakpoints and bookmarks lines.
|
||||
If [code]true[/code], executing lines are marked in the gutter. This gutter is shared with breakpoints and bookmarks. See [method set_line_as_executing].
|
||||
</member>
|
||||
<member name="gutters_draw_fold_gutter" type="bool" setter="set_draw_fold_gutter" getter="is_drawing_fold_gutter" default="false">
|
||||
Sets if foldable lines icons should be drawn in the gutter.
|
||||
If [code]true[/code], the fold gutter is drawn. In this gutter, the [theme_item can_fold_code_region] icon is drawn for each foldable line (see [method can_fold_line]) and the [theme_item folded_code_region] icon is drawn for each folded line (see [method is_line_folded]). These icons can be clicked to toggle the fold state, see [method toggle_foldable_line]. [member line_folding] must be [code]true[/code] to show icons.
|
||||
</member>
|
||||
<member name="gutters_draw_line_numbers" type="bool" setter="set_draw_line_numbers" getter="is_draw_line_numbers_enabled" default="false">
|
||||
Sets if line numbers should be drawn in the gutter.
|
||||
If [code]true[/code], the line number gutter is drawn. Line numbers start at [code]1[/code] and are incremented for each line of text. Clicking and dragging in the line number gutter will select entire lines of text.
|
||||
</member>
|
||||
<member name="gutters_zero_pad_line_numbers" type="bool" setter="set_line_numbers_zero_padded" getter="is_line_numbers_zero_padded" default="false">
|
||||
Sets if line numbers drawn in the gutter are zero padded.
|
||||
If [code]true[/code], line numbers drawn in the gutter are zero padded based on the total line count. Requires [member gutters_draw_line_numbers] to be set to [code]true[/code].
|
||||
</member>
|
||||
<member name="indent_automatic" type="bool" setter="set_auto_indent_enabled" getter="is_auto_indent_enabled" default="false">
|
||||
Sets whether automatic indent are enabled, this will add an extra indent if a prefix or brace is found.
|
||||
If [code]true[/code], an extra indent is automatically inserted when a new line is added and a prefix in [member indent_automatic_prefixes] is found. If a brace pair opening key is found, the matching closing brace will be moved to another new line (see [member auto_brace_completion_pairs]).
|
||||
</member>
|
||||
<member name="indent_automatic_prefixes" type="String[]" setter="set_auto_indent_prefixes" getter="get_auto_indent_prefixes" default="[":", "{", "[", "("]">
|
||||
Prefixes to trigger an automatic indent.
|
||||
Prefixes to trigger an automatic indent. Used when [member indent_automatic] is set to [code]true[/code].
|
||||
</member>
|
||||
<member name="indent_size" type="int" setter="set_indent_size" getter="get_indent_size" default="4">
|
||||
Size of the tabulation indent (one [kbd]Tab[/kbd] press) in characters. If [member indent_use_spaces] is enabled the number of spaces to use.
|
||||
@ -579,7 +579,7 @@
|
||||
</member>
|
||||
<member name="layout_direction" type="int" setter="set_layout_direction" getter="get_layout_direction" overrides="Control" enum="Control.LayoutDirection" default="2" />
|
||||
<member name="line_folding" type="bool" setter="set_line_folding_enabled" getter="is_line_folding_enabled" default="false">
|
||||
Sets whether line folding is allowed.
|
||||
If [code]true[/code], lines can be folded. Otherwise, line folding methods like [method fold_line] will not work and [method can_fold_line] will always return [code]false[/code]. See [member gutters_draw_fold_gutter].
|
||||
</member>
|
||||
<member name="line_length_guidelines" type="int[]" setter="set_line_length_guidelines" getter="get_line_length_guidelines" default="[]">
|
||||
Draws vertical lines at the provided columns. The first entry is considered a main hard guideline and is draw more prominently.
|
||||
@ -598,7 +598,7 @@
|
||||
</signal>
|
||||
<signal name="code_completion_requested">
|
||||
<description>
|
||||
Emitted when the user requests code completion.
|
||||
Emitted when the user requests code completion. This signal will not be sent if [method _request_code_completion] is overridden or [member code_completion_enabled] is [code]false[/code].
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="symbol_lookup">
|
||||
|
@ -204,6 +204,9 @@
|
||||
<member name="bake_interval" type="float" setter="set_bake_interval" getter="get_bake_interval" default="0.2">
|
||||
The distance in meters between two adjacent cached points. Changing it forces the cache to be recomputed the next time the [method get_baked_points] or [method get_baked_length] function is called. The smaller the distance, the more points in the cache and the more memory it will consume, so use with care.
|
||||
</member>
|
||||
<member name="closed" type="bool" setter="set_closed" getter="is_closed" default="false">
|
||||
If [code]true[/code], and the curve has more than 2 control points, the last point and the first one will be connected in a loop.
|
||||
</member>
|
||||
<member name="point_count" type="int" setter="set_point_count" getter="get_point_count" default="0">
|
||||
The number of points describing the curve.
|
||||
</member>
|
||||
|
@ -237,7 +237,7 @@
|
||||
<return type="int" />
|
||||
<param index="0" name="rect" type="Rect2" />
|
||||
<description>
|
||||
Returns index of the screen which contains specified rectangle.
|
||||
Returns the index of the screen that overlaps the most with the given rectangle. Returns [code]-1[/code] if the rectangle doesn't overlap with any screen or has no area.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_swap_cancel_ok">
|
||||
|
@ -1275,7 +1275,7 @@
|
||||
If [code]true[/code], adds [url=$DOCS_URL/tutorials/scripting/gdscript/static_typing.html]GDScript static typing[/url] hints such as [code]-> void[/code] and [code]: int[/code] when using code autocompletion or when creating onready variables by drag and dropping nodes into the script editor while pressing the [kbd]Ctrl[/kbd] key. If [code]true[/code], newly created scripts will also automatically have type hints added to their method parameters and return types.
|
||||
</member>
|
||||
<member name="text_editor/completion/auto_brace_complete" type="bool" setter="" getter="">
|
||||
If [code]true[/code], automatically completes braces when making use of code completion.
|
||||
If [code]true[/code], automatically inserts the matching closing brace when the opening brace is inserted by typing or autocompletion. Also automatically removes the closing brace when pressing [kbd]Backspace[/kbd] on the opening brace. This includes brackets ([code]()[/code], [code][][/code], [code]{}[/code]), string quotation marks ([code]''[/code], [code]""[/code]), and comments ([code]/**/[/code]) if the language supports it.
|
||||
</member>
|
||||
<member name="text_editor/completion/code_complete_delay" type="float" setter="" getter="">
|
||||
The delay in seconds after which autocompletion suggestions should be displayed when the user stops typing.
|
||||
|
@ -40,6 +40,11 @@
|
||||
Emitted when the spinner/slider is ungrabbed.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="updown_pressed">
|
||||
<description>
|
||||
Emitted when the updown button is pressed.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="value_focus_entered">
|
||||
<description>
|
||||
Emitted when the value form gains focus.
|
||||
|
@ -99,6 +99,14 @@
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="_get_comments" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="msgids_comment" type="String[]" />
|
||||
<param index="1" name="msgids_context_plural_comment" type="String[]" />
|
||||
<description>
|
||||
If overridden, called after [method _parse_file] to get comments for the parsed entries. This method should fill the arrays with the same number of elements and in the same order as [method _parse_file].
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_recognized_extensions" qualifiers="virtual const">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
|
@ -4,7 +4,7 @@
|
||||
Provides methods for file reading and writing operations.
|
||||
</brief_description>
|
||||
<description>
|
||||
This class can be used to permanently store data in the user device's file system and to read from it. This is useful for store game save data or player configuration files.
|
||||
This class can be used to permanently store data in the user device's file system and to read from it. This is useful for storing game save data or player configuration files.
|
||||
Here's a sample on how to write and read from a file:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
|
@ -2545,6 +2545,13 @@
|
||||
[b]Note:[/b] If the buffer is in the engine's internal cache, it will have to be fetched from GPU memory and possibly decompressed. This means [method multimesh_get_buffer] is potentially a slow operation and should be avoided whenever possible.
|
||||
</description>
|
||||
</method>
|
||||
<method name="multimesh_get_buffer_rd_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<param index="0" name="multimesh" type="RID" />
|
||||
<description>
|
||||
Returns the [RenderingDevice] [RID] handle of the [MultiMesh], which can be used as any other buffer on the Rendering Device.
|
||||
</description>
|
||||
</method>
|
||||
<method name="multimesh_get_custom_aabb" qualifiers="const">
|
||||
<return type="AABB" />
|
||||
<param index="0" name="multimesh" type="RID" />
|
||||
@ -3314,7 +3321,8 @@
|
||||
<return type="void" />
|
||||
<param index="0" name="generate" type="bool" />
|
||||
<description>
|
||||
This method is currently unimplemented and does nothing if called with [param generate] set to [code]true[/code].
|
||||
If [param generate] is [code]true[/code], generates debug wireframes for all meshes that are loaded when using the Compatibility renderer. By default, the engine does not generate debug wireframes at runtime, since they slow down loading of assets and take up VRAM.
|
||||
[b]Note:[/b] You must call this method before loading any meshes when using the Compatibility renderer, otherwise wireframes will not be used.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_default_clear_color">
|
||||
@ -5047,6 +5055,7 @@
|
||||
</constant>
|
||||
<constant name="VIEWPORT_DEBUG_DRAW_WIREFRAME" value="4" enum="ViewportDebugDraw">
|
||||
Debug draw draws objects in wireframe.
|
||||
[b]Note:[/b] [method set_debug_generate_wireframes] must be called before loading any meshes for wireframes to be visible when using the Compatibility renderer.
|
||||
</constant>
|
||||
<constant name="VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER" value="5" enum="ViewportDebugDraw">
|
||||
Normal buffer is drawn instead of regular scene so you can see the per-pixel normals that will be used by post-processing effects.
|
||||
|
@ -265,7 +265,7 @@
|
||||
<return type="int" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns the first column containing a non-whitespace character.
|
||||
Returns the first column containing a non-whitespace character on the given line. If there is only whitespace, returns the number of characters.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_first_visible_line" qualifiers="const">
|
||||
@ -311,7 +311,7 @@
|
||||
<return type="int" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns the number of spaces and [code]tab * tab_size[/code] before the first char.
|
||||
Returns the indent level of the given line. This is the number of spaces and tabs at the beginning of the line, with the tabs taking the tab size into account (see [method get_tab_size]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_last_full_visible_line" qualifiers="const">
|
||||
@ -343,7 +343,7 @@
|
||||
<return type="Color" />
|
||||
<param index="0" name="line" type="int" />
|
||||
<description>
|
||||
Returns the current background color of the line. [code]Color(0, 0, 0, 0)[/code] is returned if no color is set.
|
||||
Returns the custom background color of the given line. If no color is set, returns [code]Color(0, 0, 0, 0)[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_column_at_pos" qualifiers="const">
|
||||
@ -428,7 +428,7 @@
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="column" type="int" />
|
||||
<description>
|
||||
Returns the wrap index of the given line column.
|
||||
Returns the wrap index of the given column on the given line. This ranges from [code]0[/code] to [method get_line_wrap_count].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_wrapped_text" qualifiers="const">
|
||||
@ -655,7 +655,7 @@
|
||||
<method name="get_total_visible_line_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the number of lines that may be drawn.
|
||||
Returns the total number of lines in the text. This includes wrapped lines and excludes folded lines. If [member wrap_mode] is set to [constant LINE_WRAPPING_NONE] and no lines are folded (see [method CodeEdit.is_line_folded]) then this is equivalent to [method get_line_count]. See [method get_visible_line_count_in_range] for a limited range of lines.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_v_scroll_bar" qualifiers="const">
|
||||
@ -673,7 +673,7 @@
|
||||
<method name="get_visible_line_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the number of visible lines, including wrapped text.
|
||||
Returns the number of lines that can visually fit, rounded down, based on this control's height.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_visible_line_count_in_range" qualifiers="const">
|
||||
@ -681,7 +681,7 @@
|
||||
<param index="0" name="from_line" type="int" />
|
||||
<param index="1" name="to_line" type="int" />
|
||||
<description>
|
||||
Returns the total number of visible + wrapped lines between the two lines.
|
||||
Returns the total number of lines between [param from_line] and [param to_line] (inclusive) in the text. This includes wrapped lines and excludes folded lines. If the range covers all lines it is equivalent to [method get_total_visible_line_count].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_word_at_pos" qualifiers="const">
|
||||
@ -777,21 +777,21 @@
|
||||
<return type="bool" />
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<description>
|
||||
Returns whether the gutter is clickable.
|
||||
Returns [code]true[/code] if the gutter at the given index is clickable. See [method set_gutter_clickable].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_gutter_drawn" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<description>
|
||||
Returns whether the gutter is currently drawn.
|
||||
Returns [code]true[/code] if the gutter at the given index is currently drawn. See [method set_gutter_draw].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_gutter_overwritable" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<description>
|
||||
Returns whether the gutter is overwritable.
|
||||
Returns [code]true[/code] if the gutter at the given index is overwritable. See [method set_gutter_overwritable].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_in_mulitcaret_edit" qualifiers="const">
|
||||
@ -805,7 +805,7 @@
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="gutter" type="int" />
|
||||
<description>
|
||||
Returns whether the gutter on the given line is clickable.
|
||||
Returns [code]true[/code] if the gutter at the given index on the given line is clickable. See [method set_line_gutter_clickable].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_line_wrapped" qualifiers="const">
|
||||
@ -818,7 +818,7 @@
|
||||
<method name="is_menu_visible" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns whether the menu is visible. Use this instead of [code]get_menu().visible[/code] to improve performance (so the creation of the menu is avoided).
|
||||
Returns [code]true[/code] if the menu is visible. Use this instead of [code]get_menu().visible[/code] to improve performance (so the creation of the menu is avoided). See [method get_menu].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_mouse_over_selection" qualifiers="const">
|
||||
@ -826,13 +826,13 @@
|
||||
<param index="0" name="edges" type="bool" />
|
||||
<param index="1" name="caret_index" type="int" default="-1" />
|
||||
<description>
|
||||
Returns whether the mouse is over selection. If [param edges] is [code]true[/code], the edges are considered part of the selection.
|
||||
Returns [code]true[/code] if the mouse is over a selection. If [param edges] is [code]true[/code], the edges are considered part of the selection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_overtype_mode_enabled" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns whether the user is in overtype mode.
|
||||
Returns [code]true[/code] if overtype mode is enabled. See [method set_overtype_mode_enabled].
|
||||
</description>
|
||||
</method>
|
||||
<method name="menu_option">
|
||||
@ -847,7 +847,7 @@
|
||||
<param index="0" name="from_line" type="int" />
|
||||
<param index="1" name="to_line" type="int" />
|
||||
<description>
|
||||
Merge the gutters from [param from_line] into [param to_line]. Only overwritable gutters will be copied.
|
||||
Merge the gutters from [param from_line] into [param to_line]. Only overwritable gutters will be copied. See [method set_gutter_overwritable].
|
||||
</description>
|
||||
</method>
|
||||
<method name="merge_overlapping_carets">
|
||||
@ -898,7 +898,7 @@
|
||||
<return type="void" />
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<description>
|
||||
Removes the gutter from this [TextEdit].
|
||||
Removes the gutter at the given index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_line_at">
|
||||
@ -1013,7 +1013,7 @@
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<param index="1" name="clickable" type="bool" />
|
||||
<description>
|
||||
Sets the gutter as clickable. This will change the mouse cursor to a pointing hand when hovering over the gutter.
|
||||
If [code]true[/code], the mouse cursor will change to a pointing hand ([constant Control.CURSOR_POINTING_HAND]) when hovering over the gutter at the given index. See [method is_gutter_clickable] and [method set_line_gutter_clickable].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_custom_draw">
|
||||
@ -1021,7 +1021,7 @@
|
||||
<param index="0" name="column" type="int" />
|
||||
<param index="1" name="draw_callback" type="Callable" />
|
||||
<description>
|
||||
Set a custom draw method for the gutter. The callback method must take the following args: [code]line: int, gutter: int, Area: Rect2[/code]. This only works when the gutter type is [constant GUTTER_TYPE_CUSTOM] (see [method set_gutter_type]).
|
||||
Set a custom draw callback for the gutter at the given index. [param draw_callback] must take the following arguments: A line index [int], a gutter index [int], and an area [Rect2]. This callback only works when the gutter type is [constant GUTTER_TYPE_CUSTOM] (see [method set_gutter_type]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_draw">
|
||||
@ -1029,7 +1029,7 @@
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<param index="1" name="draw" type="bool" />
|
||||
<description>
|
||||
Sets whether the gutter should be drawn.
|
||||
If [code]true[/code], the gutter at the given index is drawn. The gutter type ([method set_gutter_type]) determines how it is drawn. See [method is_gutter_drawn].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_name">
|
||||
@ -1037,7 +1037,7 @@
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<param index="1" name="name" type="String" />
|
||||
<description>
|
||||
Sets the name of the gutter.
|
||||
Sets the name of the gutter at the given index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_overwritable">
|
||||
@ -1045,7 +1045,7 @@
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<param index="1" name="overwritable" type="bool" />
|
||||
<description>
|
||||
Sets the gutter to overwritable. See [method merge_gutters].
|
||||
If [code]true[/code], the line data of the gutter at the given index can be overridden when using [method merge_gutters]. See [method is_gutter_overwritable].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_type">
|
||||
@ -1053,7 +1053,7 @@
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<param index="1" name="type" type="int" enum="TextEdit.GutterType" />
|
||||
<description>
|
||||
Sets the type of gutter. Gutters can contain icons, text, or custom visuals. See [enum TextEdit.GutterType] for options.
|
||||
Sets the type of gutter at the given index. Gutters can contain icons, text, or custom visuals. See [enum TextEdit.GutterType] for options.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_gutter_width">
|
||||
@ -1061,7 +1061,7 @@
|
||||
<param index="0" name="gutter" type="int" />
|
||||
<param index="1" name="width" type="int" />
|
||||
<description>
|
||||
Set the width of the gutter.
|
||||
Set the width of the gutter at the given index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_line">
|
||||
@ -1102,7 +1102,7 @@
|
||||
<param index="0" name="line" type="int" />
|
||||
<param index="1" name="color" type="Color" />
|
||||
<description>
|
||||
Sets the current background color of the line. Set to [code]Color(0, 0, 0, 0)[/code] for no color.
|
||||
Sets the custom background color of the given line. If transparent, this color is applied on top of the default background color (See [theme_item background_color]). If set to [code]Color(0, 0, 0, 0)[/code], no additional color is applied.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_line_gutter_clickable">
|
||||
@ -1111,7 +1111,7 @@
|
||||
<param index="1" name="gutter" type="int" />
|
||||
<param index="2" name="clickable" type="bool" />
|
||||
<description>
|
||||
If [param clickable] is [code]true[/code], makes the [param gutter] on [param line] clickable. See [signal gutter_clicked].
|
||||
If [param clickable] is [code]true[/code], makes the [param gutter] on the given [param line] clickable. This is like [method set_gutter_clickable], but for a single line. If [method is_gutter_clickable] is [code]true[/code], this will not have any effect. See [method is_line_gutter_clickable] and [signal gutter_clicked].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_line_gutter_icon">
|
||||
@ -1154,7 +1154,7 @@
|
||||
<return type="void" />
|
||||
<param index="0" name="enabled" type="bool" />
|
||||
<description>
|
||||
If [code]true[/code], sets the user into overtype mode. When the user types in this mode, it will override existing text.
|
||||
If [code]true[/code], enables overtype mode. In this mode, typing overrides existing text instead of inserting text. The [member ProjectSettings.input/ui_text_toggle_insert_mode] action toggles overtype mode. See [method is_overtype_mode_enabled].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_search_flags">
|
||||
@ -1269,7 +1269,7 @@
|
||||
If [code]false[/code], the context menu ignores mouse location.
|
||||
</member>
|
||||
<member name="caret_multiple" type="bool" setter="set_multiple_carets_enabled" getter="is_multiple_carets_enabled" default="true">
|
||||
Sets if multiple carets are allowed.
|
||||
If [code]true[/code], multiple carets are allowed. Left-clicking with [kbd]Alt[/kbd] adds a new caret. See [method add_caret] and [method get_caret_count].
|
||||
</member>
|
||||
<member name="caret_type" type="int" setter="set_caret_type" getter="get_caret_type" enum="TextEdit.CaretType" default="0">
|
||||
Set the type of caret to draw.
|
||||
|
@ -42,6 +42,7 @@
|
||||
</member>
|
||||
<member name="radial_initial_angle" type="float" setter="set_radial_initial_angle" getter="get_radial_initial_angle" default="0.0">
|
||||
Starting angle 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]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees].
|
||||
[b]Note:[/b] [member radial_initial_angle] is wrapped between [code]0[/code] and [code]360[/code] degrees (inclusive).
|
||||
</member>
|
||||
<member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="1" />
|
||||
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" />
|
||||
|
@ -555,6 +555,7 @@
|
||||
</constant>
|
||||
<constant name="DEBUG_DRAW_WIREFRAME" value="4" enum="DebugDraw">
|
||||
Objects are displayed as wireframe models.
|
||||
[b]Note:[/b] [method RenderingServer.set_debug_generate_wireframes] must be called before loading any meshes for wireframes to be visible when using the Compatibility renderer.
|
||||
</constant>
|
||||
<constant name="DEBUG_DRAW_NORMAL_BUFFER" value="5" enum="DebugDraw">
|
||||
Objects are displayed without lighting information and their textures replaced by normal mapping.
|
||||
|
@ -80,7 +80,7 @@ Error AudioDriverALSA::init_output_device() {
|
||||
status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
} else {
|
||||
String device = output_device_name;
|
||||
int pos = device.find(";");
|
||||
int pos = device.find_char(';');
|
||||
if (pos != -1) {
|
||||
device = device.substr(0, pos);
|
||||
}
|
||||
|
@ -687,6 +687,8 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
|
||||
|
||||
state.current_tex = RID();
|
||||
|
||||
const uint64_t base_specialization = GLES3::Config::get_singleton()->float_texture_supported ? 0 : CanvasShaderGLES3::USE_RGBA_SHADOWS;
|
||||
|
||||
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
|
||||
// Skipping when there is no instances.
|
||||
if (state.canvas_instance_batches[i].instance_count == 0) {
|
||||
@ -705,10 +707,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
|
||||
}
|
||||
|
||||
GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data;
|
||||
CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant;
|
||||
uint64_t specialization = 0;
|
||||
specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled);
|
||||
specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1;
|
||||
CanvasShaderGLES3::ShaderVariant variant = CanvasShaderGLES3::MODE_DEFAULT;
|
||||
uint64_t specialization = state.canvas_instance_batches[i].specialization;
|
||||
specialization |= base_specialization;
|
||||
RID shader_version = data.canvas_shader_default_version;
|
||||
|
||||
if (material_data) {
|
||||
@ -810,6 +811,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
|
||||
|
||||
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) {
|
||||
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
|
||||
const uint64_t specialization_command_mask = ~(CanvasShaderGLES3::USE_NINEPATCH | CanvasShaderGLES3::USE_PRIMITIVE | CanvasShaderGLES3::USE_ATTRIBUTES | CanvasShaderGLES3::USE_INSTANCING);
|
||||
|
||||
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
|
||||
_new_batch(r_batch_broken);
|
||||
@ -868,9 +870,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
|
||||
bool lights_disabled = light_count == 0 && !state.using_directional_lights;
|
||||
|
||||
if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) {
|
||||
if (lights_disabled != bool(state.canvas_instance_batches[state.current_batch_index].specialization & CanvasShaderGLES3::DISABLE_LIGHTING)) {
|
||||
_new_batch(r_batch_broken);
|
||||
state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization ^= CanvasShaderGLES3::DISABLE_LIGHTING;
|
||||
}
|
||||
|
||||
const Item::Command *c = p_item->commands;
|
||||
@ -936,7 +938,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
state.canvas_instance_batches[state.current_batch_index].tex = rect->texture;
|
||||
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT;
|
||||
state.canvas_instance_batches[state.current_batch_index].command = c;
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
|
||||
}
|
||||
|
||||
_prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
|
||||
@ -1026,7 +1028,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
state.canvas_instance_batches[state.current_batch_index].tex = np->texture;
|
||||
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH;
|
||||
state.canvas_instance_batches[state.current_batch_index].command = c;
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_NINEPATCH;
|
||||
}
|
||||
|
||||
_prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
|
||||
@ -1092,7 +1095,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture;
|
||||
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON;
|
||||
state.canvas_instance_batches[state.current_batch_index].command = c;
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
|
||||
|
||||
_prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
|
||||
|
||||
@ -1119,7 +1123,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count;
|
||||
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE;
|
||||
state.canvas_instance_batches[state.current_batch_index].command = c;
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_PRIMITIVE;
|
||||
}
|
||||
|
||||
_prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
|
||||
@ -1164,7 +1169,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
_new_batch(r_batch_broken);
|
||||
|
||||
Color modulate(1, 1, 1, 1);
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
|
||||
if (c->type == Item::Command::TYPE_MESH) {
|
||||
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
|
||||
state.canvas_instance_batches[state.current_batch_index].tex = m->texture;
|
||||
@ -1174,7 +1180,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
|
||||
const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
|
||||
state.canvas_instance_batches[state.current_batch_index].tex = mm->texture;
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
|
||||
|
||||
if (GLES3::MeshStorage::get_singleton()->multimesh_uses_colors(mm->multimesh)) {
|
||||
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
|
||||
@ -1189,7 +1195,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
|
||||
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
|
||||
RID particles = pt->particles;
|
||||
state.canvas_instance_batches[state.current_batch_index].tex = pt->texture;
|
||||
state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
|
||||
state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
|
||||
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
|
||||
state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
|
||||
|
||||
|
@ -273,14 +273,12 @@ public:
|
||||
|
||||
RID material;
|
||||
GLES3::CanvasMaterialData *material_data = nullptr;
|
||||
CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD;
|
||||
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
|
||||
uint64_t specialization = 0;
|
||||
|
||||
const Item::Command *command = nullptr;
|
||||
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
|
||||
uint32_t primitive_points = 0;
|
||||
|
||||
bool lights_disabled = false;
|
||||
};
|
||||
|
||||
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
|
||||
|
@ -238,7 +238,7 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry
|
||||
|
||||
GLES3::SceneMaterialData *material_shadow = nullptr;
|
||||
void *surface_shadow = nullptr;
|
||||
if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates) {
|
||||
if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates && !p_material->shader_data->wireframe) {
|
||||
flags |= GeometryInstanceSurface::FLAG_USES_SHARED_SHADOW_MATERIAL;
|
||||
material_shadow = static_cast<GLES3::SceneMaterialData *>(GLES3::MaterialStorage::get_singleton()->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL));
|
||||
|
||||
@ -3157,7 +3157,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
||||
}
|
||||
|
||||
bool use_wireframe = false;
|
||||
if (p_params->force_wireframe) {
|
||||
if (p_params->force_wireframe || shader->wireframe) {
|
||||
GLuint wireframe_index_array_gl = mesh_storage->mesh_surface_get_index_buffer_wireframe(mesh_surface);
|
||||
if (wireframe_index_array_gl) {
|
||||
index_array_gl = wireframe_index_array_gl;
|
||||
|
@ -1,17 +1,16 @@
|
||||
/* clang-format off */
|
||||
#[modes]
|
||||
|
||||
mode_quad =
|
||||
mode_ninepatch = #define USE_NINEPATCH
|
||||
mode_primitive = #define USE_PRIMITIVE
|
||||
mode_attributes = #define USE_ATTRIBUTES
|
||||
mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
|
||||
mode_default =
|
||||
|
||||
#[specializations]
|
||||
|
||||
DISABLE_LIGHTING = true
|
||||
USE_RGBA_SHADOWS = false
|
||||
SINGLE_INSTANCE = false
|
||||
USE_NINEPATCH = false
|
||||
USE_PRIMITIVE = false
|
||||
USE_ATTRIBUTES = false
|
||||
USE_INSTANCING = false
|
||||
|
||||
#[vertex]
|
||||
|
||||
|
@ -2,17 +2,15 @@
|
||||
#[modes]
|
||||
|
||||
mode_background =
|
||||
mode_half_res = #define USE_HALF_RES_PASS
|
||||
mode_quarter_res = #define USE_QUARTER_RES_PASS
|
||||
mode_cubemap = #define USE_CUBEMAP_PASS
|
||||
mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS
|
||||
mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS
|
||||
|
||||
#[specializations]
|
||||
|
||||
USE_MULTIVIEW = false
|
||||
USE_INVERTED_Y = true
|
||||
APPLY_TONEMAPPING = true
|
||||
USE_QUARTER_RES_PASS = false
|
||||
USE_HALF_RES_PASS = false
|
||||
|
||||
#[vertex]
|
||||
|
||||
|
@ -1974,6 +1974,10 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_
|
||||
}
|
||||
}
|
||||
|
||||
RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const {
|
||||
ERR_FAIL_V_MSG(RID(), "GLES3 does not contain a Rid for the multimesh buffer.");
|
||||
}
|
||||
|
||||
Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
ERR_FAIL_NULL_V(multimesh, Vector<float>());
|
||||
|
@ -517,6 +517,7 @@ public:
|
||||
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
|
||||
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
|
||||
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
|
||||
virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override;
|
||||
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;
|
||||
|
||||
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
|
||||
|
@ -1032,7 +1032,7 @@ void RenderingDeviceDriverMetal::framebuffer_free(FramebufferID p_framebuffer) {
|
||||
|
||||
#pragma mark - Shader
|
||||
|
||||
const uint32_t SHADER_BINARY_VERSION = 2;
|
||||
const uint32_t SHADER_BINARY_VERSION = 3;
|
||||
|
||||
// region Serialization
|
||||
|
||||
@ -1336,23 +1336,32 @@ struct ComputeSize {
|
||||
|
||||
struct ShaderStageData {
|
||||
RD::ShaderStage stage = RD::ShaderStage::SHADER_STAGE_MAX;
|
||||
uint32_t is_position_invariant = UINT32_MAX;
|
||||
uint32_t supports_fast_math = UINT32_MAX;
|
||||
CharString entry_point_name;
|
||||
CharString source;
|
||||
|
||||
size_t serialize_size() const {
|
||||
int comp_size = Compression::get_max_compressed_buffer_size(source.length(), Compression::MODE_ZSTD);
|
||||
return sizeof(uint32_t) // Stage.
|
||||
+ sizeof(uint32_t) /* entry_point_name.utf8().length */ + entry_point_name.length() + sizeof(uint32_t) /* uncompressed size */ + sizeof(uint32_t) /* compressed size */ + comp_size;
|
||||
+ sizeof(uint32_t) // is_position_invariant
|
||||
+ sizeof(uint32_t) // supports_fast_math
|
||||
+ sizeof(uint32_t) /* entry_point_name.utf8().length */
|
||||
+ entry_point_name.length() + sizeof(uint32_t) /* uncompressed size */ + sizeof(uint32_t) /* compressed size */ + comp_size;
|
||||
}
|
||||
|
||||
void serialize(BufWriter &p_writer) const {
|
||||
p_writer.write((uint32_t)stage);
|
||||
p_writer.write(is_position_invariant);
|
||||
p_writer.write(supports_fast_math);
|
||||
p_writer.write(entry_point_name);
|
||||
p_writer.write_compressed(source);
|
||||
}
|
||||
|
||||
void deserialize(BufReader &p_reader) {
|
||||
p_reader.read((uint32_t &)stage);
|
||||
p_reader.read(is_position_invariant);
|
||||
p_reader.read(supports_fast_math);
|
||||
p_reader.read(entry_point_name);
|
||||
p_reader.read_compressed(source);
|
||||
}
|
||||
@ -2011,7 +2020,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
|
||||
|
||||
ERR_FAIL_COND_V_MSG(compiler.get_entry_points_and_stages().size() != 1, Result(), "Expected a single entry point and stage.");
|
||||
|
||||
EntryPoint &entry_point_stage = compiler.get_entry_points_and_stages().front();
|
||||
SmallVector<EntryPoint> entry_pts_stages = compiler.get_entry_points_and_stages();
|
||||
EntryPoint &entry_point_stage = entry_pts_stages.front();
|
||||
SPIREntryPoint &entry_point = compiler.get_entry_point(entry_point_stage.name, entry_point_stage.execution_model);
|
||||
|
||||
// Process specialization constants.
|
||||
@ -2293,6 +2303,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
|
||||
|
||||
ShaderStageData stage_data;
|
||||
stage_data.stage = v.shader_stage;
|
||||
stage_data.is_position_invariant = compiler.is_position_invariant();
|
||||
stage_data.supports_fast_math = !entry_point.flags.get(spv::ExecutionModeSignedZeroInfNanPreserve);
|
||||
stage_data.entry_point_name = entry_point.name.c_str();
|
||||
stage_data.source = source.c_str();
|
||||
bin_data.stages.push_back(stage_data);
|
||||
@ -2365,7 +2377,8 @@ RDD::ShaderID RenderingDeviceDriverMetal::shader_create_from_bytecode(const Vect
|
||||
ShaderCacheEntry *cd = memnew(ShaderCacheEntry(*this, key));
|
||||
cd->name = binary_data.shader_name;
|
||||
cd->stage = shader_data.stage;
|
||||
|
||||
options.preserveInvariance = shader_data.is_position_invariant;
|
||||
options.fastMathEnabled = YES;
|
||||
MDLibrary *library = [MDLibrary newLibraryWithCacheEntry:cd
|
||||
device:device
|
||||
source:source
|
||||
|
@ -28,23 +28,10 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#if defined(UNIX_ENABLED)
|
||||
|
||||
#include "ip_unix.h"
|
||||
|
||||
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <iphlpapi.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#else // UNIX
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
@ -67,8 +54,6 @@
|
||||
|
||||
#include <net/if.h> // Order is important on OpenBSD, leave as last.
|
||||
|
||||
#endif // UNIX
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
|
||||
@ -108,7 +93,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
|
||||
}
|
||||
|
||||
if (result == nullptr || result->ai_addr == nullptr) {
|
||||
print_verbose("Invalid response from getaddrinfo");
|
||||
print_verbose("Invalid response from getaddrinfo.");
|
||||
if (result) {
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
@ -132,56 +117,6 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
|
||||
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
|
||||
ULONG buf_size = 1024;
|
||||
IP_ADAPTER_ADDRESSES *addrs;
|
||||
|
||||
while (true) {
|
||||
addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
|
||||
int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
nullptr, addrs, &buf_size);
|
||||
if (err == NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
memfree(addrs);
|
||||
if (err == ERROR_BUFFER_OVERFLOW) {
|
||||
continue; // will go back and alloc the right size
|
||||
}
|
||||
|
||||
ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
|
||||
}
|
||||
|
||||
IP_ADAPTER_ADDRESSES *adapter = addrs;
|
||||
|
||||
while (adapter != nullptr) {
|
||||
Interface_Info info;
|
||||
info.name = adapter->AdapterName;
|
||||
info.name_friendly = adapter->FriendlyName;
|
||||
info.index = String::num_uint64(adapter->IfIndex);
|
||||
|
||||
IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
|
||||
while (address != nullptr) {
|
||||
int family = address->Address.lpSockaddr->sa_family;
|
||||
if (family != AF_INET && family != AF_INET6) {
|
||||
continue;
|
||||
}
|
||||
info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
|
||||
address = address->Next;
|
||||
}
|
||||
adapter = adapter->Next;
|
||||
// Only add interface if it has at least one IP
|
||||
if (info.ip_addresses.size() > 0) {
|
||||
r_interfaces->insert(info.name, info);
|
||||
}
|
||||
}
|
||||
|
||||
memfree(addrs);
|
||||
}
|
||||
|
||||
#else // UNIX
|
||||
|
||||
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
|
||||
struct ifaddrs *ifAddrStruct = nullptr;
|
||||
struct ifaddrs *ifa = nullptr;
|
||||
@ -219,8 +154,6 @@ void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces)
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNIX
|
||||
|
||||
void IPUnix::make_default() {
|
||||
_create = _create_unix;
|
||||
}
|
||||
@ -232,4 +165,4 @@ IP *IPUnix::_create_unix() {
|
||||
IPUnix::IPUnix() {
|
||||
}
|
||||
|
||||
#endif // UNIX_ENABLED || WINDOWS_ENABLED
|
||||
#endif // UNIX_ENABLED
|
||||
|
@ -31,9 +31,9 @@
|
||||
#ifndef IP_UNIX_H
|
||||
#define IP_UNIX_H
|
||||
|
||||
#include "core/io/ip.h"
|
||||
#if defined(UNIX_ENABLED)
|
||||
|
||||
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
|
||||
#include "core/io/ip.h"
|
||||
|
||||
class IPUnix : public IP {
|
||||
GDCLASS(IPUnix, IP);
|
||||
@ -49,6 +49,6 @@ public:
|
||||
IPUnix();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // UNIX_ENABLED
|
||||
|
||||
#endif // IP_UNIX_H
|
||||
|
@ -860,7 +860,7 @@ String OS_Unix::get_locale() const {
|
||||
}
|
||||
|
||||
String locale = get_environment("LANG");
|
||||
int tp = locale.find(".");
|
||||
int tp = locale.find_char('.');
|
||||
if (tp != -1) {
|
||||
locale = locale.substr(0, tp);
|
||||
}
|
||||
|
@ -2392,7 +2392,9 @@ RDD::CommandQueueFamilyID RenderingDeviceDriverVulkan::command_queue_family_get(
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(picked_family_index >= queue_family_properties.size(), CommandQueueFamilyID(), "A queue family with the requested bits could not be found.");
|
||||
if (picked_family_index >= queue_family_properties.size()) {
|
||||
return CommandQueueFamilyID();
|
||||
}
|
||||
|
||||
// Since 0 is a valid index and we use 0 as the error case, we make the index start from 1 instead.
|
||||
return CommandQueueFamilyID(picked_family_index + 1);
|
||||
|
@ -230,7 +230,7 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const {
|
||||
return cdir;
|
||||
} else {
|
||||
if (_get_root_string().is_empty()) {
|
||||
int pos = cdir.find(":");
|
||||
int pos = cdir.find_char(':');
|
||||
if (pos != -1) {
|
||||
return cdir.substr(pos + 1);
|
||||
}
|
||||
@ -344,7 +344,7 @@ String DirAccessWindows::get_filesystem_type() const {
|
||||
return "Network Share";
|
||||
}
|
||||
|
||||
int unit_end = path.find(":");
|
||||
int unit_end = path.find_char(':');
|
||||
ERR_FAIL_COND_V(unit_end == -1, String());
|
||||
String unit = path.substr(0, unit_end + 1) + "\\";
|
||||
|
||||
|
@ -64,7 +64,7 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) {
|
||||
// Check for invalid operating system file.
|
||||
String fname = p_path.get_file().to_lower();
|
||||
|
||||
int dot = fname.find(".");
|
||||
int dot = fname.find_char('.');
|
||||
if (dot != -1) {
|
||||
fname = fname.substr(0, dot);
|
||||
}
|
||||
|
164
drivers/windows/ip_windows.cpp
Normal file
164
drivers/windows/ip_windows.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/**************************************************************************/
|
||||
/* ip_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
|
||||
#include "ip_windows.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <iphlpapi.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
|
||||
IPAddress ip;
|
||||
|
||||
if (p_addr->sa_family == AF_INET) {
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *)p_addr;
|
||||
ip.set_ipv4((uint8_t *)&(addr->sin_addr));
|
||||
} else if (p_addr->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
|
||||
ip.set_ipv6(addr6->sin6_addr.s6_addr);
|
||||
}
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
void IPWindows::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result = nullptr;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
if (p_type == TYPE_IPV4) {
|
||||
hints.ai_family = AF_INET;
|
||||
} else if (p_type == TYPE_IPV6) {
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_flags = 0;
|
||||
} else {
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
}
|
||||
hints.ai_flags &= ~AI_NUMERICHOST;
|
||||
|
||||
int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result);
|
||||
if (s != 0) {
|
||||
print_verbose("getaddrinfo failed! Cannot resolve hostname.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == nullptr || result->ai_addr == nullptr) {
|
||||
print_verbose("Invalid response from getaddrinfo.");
|
||||
if (result) {
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct addrinfo *next = result;
|
||||
|
||||
do {
|
||||
if (next->ai_addr == nullptr) {
|
||||
next = next->ai_next;
|
||||
continue;
|
||||
}
|
||||
IPAddress ip = _sockaddr2ip(next->ai_addr);
|
||||
if (ip.is_valid() && !r_addresses.find(ip)) {
|
||||
r_addresses.push_back(ip);
|
||||
}
|
||||
next = next->ai_next;
|
||||
} while (next);
|
||||
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
void IPWindows::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
|
||||
ULONG buf_size = 1024;
|
||||
IP_ADAPTER_ADDRESSES *addrs;
|
||||
|
||||
while (true) {
|
||||
addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
|
||||
int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
nullptr, addrs, &buf_size);
|
||||
if (err == NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
memfree(addrs);
|
||||
if (err == ERROR_BUFFER_OVERFLOW) {
|
||||
continue; // Will go back and alloc the right size.
|
||||
}
|
||||
|
||||
ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + ".");
|
||||
}
|
||||
|
||||
IP_ADAPTER_ADDRESSES *adapter = addrs;
|
||||
|
||||
while (adapter != nullptr) {
|
||||
Interface_Info info;
|
||||
info.name = adapter->AdapterName;
|
||||
info.name_friendly = adapter->FriendlyName;
|
||||
info.index = String::num_uint64(adapter->IfIndex);
|
||||
|
||||
IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
|
||||
while (address != nullptr) {
|
||||
int family = address->Address.lpSockaddr->sa_family;
|
||||
if (family != AF_INET && family != AF_INET6) {
|
||||
continue;
|
||||
}
|
||||
info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr));
|
||||
address = address->Next;
|
||||
}
|
||||
adapter = adapter->Next;
|
||||
// Only add interface if it has at least one IP.
|
||||
if (info.ip_addresses.size() > 0) {
|
||||
r_interfaces->insert(info.name, info);
|
||||
}
|
||||
}
|
||||
|
||||
memfree(addrs);
|
||||
}
|
||||
|
||||
void IPWindows::make_default() {
|
||||
_create = _create_unix;
|
||||
}
|
||||
|
||||
IP *IPWindows::_create_unix() {
|
||||
return memnew(IPWindows);
|
||||
}
|
||||
|
||||
IPWindows::IPWindows() {
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
54
drivers/windows/ip_windows.h
Normal file
54
drivers/windows/ip_windows.h
Normal file
@ -0,0 +1,54 @@
|
||||
/**************************************************************************/
|
||||
/* ip_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef IP_WINDOWS_H
|
||||
#define IP_WINDOWS_H
|
||||
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
|
||||
#include "core/io/ip.h"
|
||||
|
||||
class IPWindows : public IP {
|
||||
GDCLASS(IPWindows, IP);
|
||||
|
||||
virtual void _resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override;
|
||||
|
||||
static IP *_create_unix();
|
||||
|
||||
public:
|
||||
virtual void get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const override;
|
||||
|
||||
static void make_default();
|
||||
IPWindows();
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
||||
|
||||
#endif // IP_WINDOWS_H
|
@ -275,7 +275,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
||||
}
|
||||
|
||||
String base_path = animation->track_get_path(i);
|
||||
int end = base_path.find(":");
|
||||
int end = base_path.find_char(':');
|
||||
if (end != -1) {
|
||||
base_path = base_path.substr(0, end + 1);
|
||||
}
|
||||
|
@ -2191,7 +2191,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
||||
offset = offset * scale + limit;
|
||||
Color marker_color = animation->get_marker_color(marker);
|
||||
marker_color.a = 0.2;
|
||||
draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color);
|
||||
draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color, Math::round(EDSCALE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3647,7 +3647,7 @@ void AnimationTrackEditGroup::_notification(int p_what) {
|
||||
offset = offset * scale + limit;
|
||||
Color marker_color = editor->get_current_animation()->get_marker_color(marker);
|
||||
marker_color.a = 0.2;
|
||||
draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color);
|
||||
draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color, Math::round(EDSCALE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4340,7 +4340,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
|
||||
if (track_path == np) {
|
||||
actual_value = value; // All good.
|
||||
} else {
|
||||
int sep = track_path.rfind(":");
|
||||
int sep = track_path.rfind_char(':');
|
||||
if (sep != -1) {
|
||||
String base_path = track_path.substr(0, sep);
|
||||
if (base_path == np) {
|
||||
@ -6495,7 +6495,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
||||
path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying.
|
||||
} else {
|
||||
text = path;
|
||||
int sep = text.find(":");
|
||||
int sep = text.find_char(':');
|
||||
if (sep != -1) {
|
||||
text = text.substr(sep + 1, text.length());
|
||||
}
|
||||
@ -7364,7 +7364,6 @@ void AnimationTrackEditor::_update_snap_unit() {
|
||||
}
|
||||
|
||||
if (timeline->is_using_fps()) {
|
||||
_clear_selection(true); // Needs to recreate a spinbox of the KeyEdit.
|
||||
snap_unit = 1.0 / step->get_value();
|
||||
} else {
|
||||
if (fps_compat->is_pressed()) {
|
||||
@ -7983,6 +7982,11 @@ AnimationTrackEditor::~AnimationTrackEditor() {
|
||||
|
||||
// AnimationTrackKeyEditEditorPlugin.
|
||||
|
||||
void AnimationTrackKeyEditEditor::_time_edit_spun() {
|
||||
_time_edit_entered();
|
||||
_time_edit_exited();
|
||||
}
|
||||
|
||||
void AnimationTrackKeyEditEditor::_time_edit_entered() {
|
||||
int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX);
|
||||
if (key == -1) {
|
||||
@ -8010,7 +8014,7 @@ void AnimationTrackKeyEditEditor::_time_edit_exited() {
|
||||
|
||||
int existing = animation->track_find_key(track, new_time, Animation::FIND_MODE_APPROX);
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS);
|
||||
undo_redo->create_action(TTR("Animation Change Keyframe Time"));
|
||||
|
||||
if (existing != -1) {
|
||||
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, animation->track_get_key_time(track, existing));
|
||||
@ -8057,6 +8061,7 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
|
||||
spinner->set_min(0);
|
||||
spinner->set_allow_greater(true);
|
||||
spinner->set_allow_lesser(true);
|
||||
add_child(spinner);
|
||||
|
||||
if (use_fps) {
|
||||
spinner->set_step(FPS_DECIMAL);
|
||||
@ -8065,14 +8070,13 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
|
||||
fps = 1.0 / fps;
|
||||
}
|
||||
spinner->set_value(key_ofs * fps);
|
||||
spinner->connect("updown_pressed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_spun), CONNECT_DEFERRED);
|
||||
} else {
|
||||
spinner->set_step(SECOND_DECIMAL);
|
||||
spinner->set_value(key_ofs);
|
||||
spinner->set_max(animation->get_length());
|
||||
}
|
||||
|
||||
add_child(spinner);
|
||||
|
||||
spinner->connect("grabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
|
||||
spinner->connect("ungrabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
|
||||
spinner->connect("value_focus_entered", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
|
||||
@ -8307,9 +8311,6 @@ void AnimationMarkerEdit::_notification(int p_what) {
|
||||
|
||||
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
|
||||
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
|
||||
int hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
|
||||
Color linecolor = color;
|
||||
linecolor.a = 0.2;
|
||||
|
||||
// SECTION PREVIEW //
|
||||
|
||||
@ -8368,31 +8369,21 @@ void AnimationMarkerEdit::_notification(int p_what) {
|
||||
|
||||
draw_key(name, scale, int(offset), is_selected, limit, limit_end);
|
||||
|
||||
const int font_size = 16;
|
||||
const int font_size = 12 * EDSCALE;
|
||||
Size2 string_size = font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size);
|
||||
if (int(offset) <= limit_end && int(offset) >= limit && should_show_all_marker_names) {
|
||||
float bottom = get_size().height + string_size.y - font->get_descent(font_size);
|
||||
float extrusion = MAX(0, offset + string_size.x - limit_end); // How much the string would extrude outside limit_end if unadjusted.
|
||||
Color marker_color = animation->get_marker_color(name);
|
||||
draw_string(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color);
|
||||
draw_string_outline(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color);
|
||||
float margin = 4 * EDSCALE;
|
||||
Point2 pos = Point2(offset - extrusion + margin, bottom + margin);
|
||||
draw_string(font, pos, name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color);
|
||||
draw_string_outline(font, pos, name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
draw_fg(limit, get_size().width - timeline->get_buttons_width());
|
||||
|
||||
// BUTTONS //
|
||||
|
||||
{
|
||||
int ofs = get_size().width - timeline->get_buttons_width();
|
||||
|
||||
draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE));
|
||||
|
||||
ofs += hsep;
|
||||
}
|
||||
|
||||
draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_ENTER:
|
||||
@ -9193,9 +9184,6 @@ void AnimationMultiMarkerKeyEdit::_get_property_list(List<PropertyInfo> *p_list)
|
||||
|
||||
// AnimationMarkerKeyEditEditorPlugin
|
||||
|
||||
void AnimationMarkerKeyEditEditor::_time_edit_entered() {
|
||||
}
|
||||
|
||||
void AnimationMarkerKeyEditEditor::_time_edit_exited() {
|
||||
real_t new_time = spinner->get_value();
|
||||
|
||||
@ -9214,7 +9202,7 @@ void AnimationMarkerKeyEditEditor::_time_edit_exited() {
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Animation Change Marker Time"), UndoRedo::MERGE_ENDS);
|
||||
undo_redo->create_action(TTR("Animation Change Marker Time"));
|
||||
|
||||
Color color = animation->get_marker_color(marker_name);
|
||||
undo_redo->add_do_method(animation.ptr(), "add_marker", marker_name, new_time);
|
||||
@ -9255,6 +9243,7 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim
|
||||
spinner->set_min(0);
|
||||
spinner->set_allow_greater(true);
|
||||
spinner->set_allow_lesser(true);
|
||||
add_child(spinner);
|
||||
|
||||
float time = animation->get_marker_time(marker_name);
|
||||
|
||||
@ -9265,17 +9254,14 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim
|
||||
fps = 1.0 / fps;
|
||||
}
|
||||
spinner->set_value(time * fps);
|
||||
spinner->connect("updown_pressed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
|
||||
} else {
|
||||
spinner->set_step(SECOND_DECIMAL);
|
||||
spinner->set_value(time);
|
||||
spinner->set_max(animation->get_length());
|
||||
}
|
||||
|
||||
add_child(spinner);
|
||||
|
||||
spinner->connect("grabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
|
||||
spinner->connect("ungrabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
|
||||
spinner->connect("value_focus_entered", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
|
||||
spinner->connect("value_focus_exited", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
|
@ -977,6 +977,7 @@ class AnimationTrackKeyEditEditor : public EditorProperty {
|
||||
Variant value;
|
||||
} key_data_cache;
|
||||
|
||||
void _time_edit_spun();
|
||||
void _time_edit_entered();
|
||||
void _time_edit_exited();
|
||||
|
||||
@ -996,7 +997,6 @@ class AnimationMarkerKeyEditEditor : public EditorProperty {
|
||||
|
||||
EditorSpinSlider *spinner = nullptr;
|
||||
|
||||
void _time_edit_entered();
|
||||
void _time_edit_exited();
|
||||
|
||||
public:
|
||||
|
@ -524,7 +524,7 @@ void ConnectDialog::set_dst_node(Node *p_node) {
|
||||
StringName ConnectDialog::get_dst_method_name() const {
|
||||
String txt = dst_method->get_text();
|
||||
if (txt.contains("(")) {
|
||||
txt = txt.left(txt.find("(")).strip_edges();
|
||||
txt = txt.left(txt.find_char('(')).strip_edges();
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
@ -160,13 +160,13 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
|
||||
|
||||
String script_path = ScriptServer::get_global_class_path(p_type);
|
||||
if (script_path.begins_with("res://addons/")) {
|
||||
int i = script_path.find("/", 13); // 13 is length of "res://addons/".
|
||||
int i = script_path.find_char('/', 13); // 13 is length of "res://addons/".
|
||||
while (i > -1) {
|
||||
const String plugin_path = script_path.substr(0, i).path_join("plugin.cfg");
|
||||
if (FileAccess::exists(plugin_path)) {
|
||||
return !EditorNode::get_singleton()->is_addon_plugin_enabled(plugin_path);
|
||||
}
|
||||
i = script_path.find("/", i + 1);
|
||||
i = script_path.find_char('/', i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
|
||||
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
|
||||
String breakpoint = E->get();
|
||||
|
||||
String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://"
|
||||
String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://"
|
||||
int line = breakpoint.substr(path.size()).to_int();
|
||||
|
||||
DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true);
|
||||
|
@ -160,9 +160,9 @@ void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_d
|
||||
} else {
|
||||
// If the script is built-in, it can be opened only if the scene is loaded in memory.
|
||||
int i = file.find("::");
|
||||
int j = file.rfind("(", i);
|
||||
int j = file.rfind_char('(', i);
|
||||
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
|
||||
file = file.substr(j + 1, file.find(")", i) - j - 1);
|
||||
file = file.substr(j + 1, file.find_char(')', i) - j - 1);
|
||||
}
|
||||
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
|
||||
stack_script = ResourceLoader::load(file);
|
||||
@ -183,9 +183,9 @@ void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_
|
||||
} else {
|
||||
// If the script is built-in, it can be opened only if the scene is loaded in memory.
|
||||
int i = file.find("::");
|
||||
int j = file.rfind("(", i);
|
||||
int j = file.rfind_char('(', i);
|
||||
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
|
||||
file = file.substr(j + 1, file.find(")", i) - j - 1);
|
||||
file = file.substr(j + 1, file.find_char(')', i) - j - 1);
|
||||
}
|
||||
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
|
||||
stack_script = ResourceLoader::load(file);
|
||||
|
@ -382,7 +382,7 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
|
||||
text = ".";
|
||||
} else {
|
||||
text = text.replace("/root/", "");
|
||||
int slash = text.find("/");
|
||||
int slash = text.find_char('/');
|
||||
if (slash < 0) {
|
||||
text = ".";
|
||||
} else {
|
||||
|
@ -471,7 +471,7 @@ void DependencyRemoveDialog::_find_localization_remaps_of_removed_files(Vector<R
|
||||
for (int j = 0; j < remap_keys.size(); j++) {
|
||||
PackedStringArray remapped_files = remaps[remap_keys[j]];
|
||||
for (int k = 0; k < remapped_files.size(); k++) {
|
||||
int splitter_pos = remapped_files[k].rfind(":");
|
||||
int splitter_pos = remapped_files[k].rfind_char(':');
|
||||
String res_path = remapped_files[k].substr(0, splitter_pos);
|
||||
if (res_path == path) {
|
||||
String locale_name = remapped_files[k].substr(splitter_pos + 1);
|
||||
|
@ -142,7 +142,7 @@ void DirectoryCreateDialog::config(const String &p_base_dir, const Callable &p_a
|
||||
validation_panel->update();
|
||||
|
||||
if (p_mode == MODE_FILE) {
|
||||
int extension_pos = p_default_name.rfind(".");
|
||||
int extension_pos = p_default_name.rfind_char('.');
|
||||
if (extension_pos > -1) {
|
||||
dir_path->select(0, extension_pos);
|
||||
return;
|
||||
|
@ -130,14 +130,14 @@ void EditorAssetInstaller::open_asset(const String &p_path, bool p_autoskip_topl
|
||||
|
||||
// Create intermediate directories if they aren't reported by unzip.
|
||||
// We are only interested in subfolders, so skip the root slash.
|
||||
int separator = source_name.find("/", 1);
|
||||
int separator = source_name.find_char('/', 1);
|
||||
while (separator != -1) {
|
||||
String dir_name = source_name.substr(0, separator + 1);
|
||||
if (!dir_name.is_empty() && !asset_files.has(dir_name)) {
|
||||
asset_files.insert(dir_name);
|
||||
}
|
||||
|
||||
separator = source_name.find("/", separator + 1);
|
||||
separator = source_name.find_char('/', separator + 1);
|
||||
}
|
||||
|
||||
if (!source_name.is_empty() && !asset_files.has(source_name)) {
|
||||
@ -214,7 +214,7 @@ void EditorAssetInstaller::_rebuild_source_tree() {
|
||||
|
||||
TreeItem *parent_item;
|
||||
|
||||
int separator = path.rfind("/");
|
||||
int separator = path.rfind_char('/');
|
||||
if (separator == -1) {
|
||||
parent_item = root;
|
||||
} else {
|
||||
@ -313,7 +313,7 @@ void EditorAssetInstaller::_rebuild_destination_tree() {
|
||||
|
||||
TreeItem *parent_item;
|
||||
|
||||
int separator = path.rfind("/");
|
||||
int separator = path.rfind_char('/');
|
||||
if (separator == -1) {
|
||||
parent_item = root;
|
||||
} else {
|
||||
|
@ -249,7 +249,7 @@ void EditorFolding::_do_object_unfolds(Object *p_object, HashSet<Ref<Resource>>
|
||||
}
|
||||
}
|
||||
} else { //path
|
||||
int last = E.name.rfind("/");
|
||||
int last = E.name.rfind_char('/');
|
||||
if (last != -1) {
|
||||
bool can_revert = EditorPropertyRevert::can_property_revert(p_object, E.name);
|
||||
if (can_revert) {
|
||||
|
@ -148,7 +148,7 @@ static String _contextualize_class_specifier(const String &p_class_specifier, co
|
||||
|
||||
// Here equal length + begins_with from above implies p_class_specifier == p_edited_class :)
|
||||
if (p_class_specifier.length() == p_edited_class.length()) {
|
||||
int rfind = p_class_specifier.rfind(".");
|
||||
int rfind = p_class_specifier.rfind_char('.');
|
||||
if (rfind == -1) { // Single identifier
|
||||
return p_class_specifier;
|
||||
}
|
||||
@ -234,7 +234,7 @@ void EditorHelp::_class_desc_select(const String &p_select) {
|
||||
enum_class_name = "@GlobalScope";
|
||||
enum_name = link;
|
||||
} else {
|
||||
const int dot_pos = link.rfind(".");
|
||||
const int dot_pos = link.rfind_char('.');
|
||||
if (dot_pos >= 0) {
|
||||
enum_class_name = link.left(dot_pos);
|
||||
enum_name = link.substr(dot_pos + 1);
|
||||
@ -3252,7 +3252,7 @@ EditorHelpBit::HelpData EditorHelpBit::_get_property_help_data(const StringName
|
||||
enum_class_name = "@GlobalScope";
|
||||
enum_name = property.enumeration;
|
||||
} else {
|
||||
const int dot_pos = property.enumeration.rfind(".");
|
||||
const int dot_pos = property.enumeration.rfind_char('.');
|
||||
if (dot_pos >= 0) {
|
||||
enum_class_name = property.enumeration.left(dot_pos);
|
||||
enum_name = property.enumeration.substr(dot_pos + 1);
|
||||
@ -3619,7 +3619,7 @@ void EditorHelpBit::_meta_clicked(const String &p_select) {
|
||||
enum_class_name = "@GlobalScope";
|
||||
enum_name = link;
|
||||
} else {
|
||||
const int dot_pos = link.rfind(".");
|
||||
const int dot_pos = link.rfind_char('.');
|
||||
if (dot_pos >= 0) {
|
||||
enum_class_name = link.left(dot_pos);
|
||||
enum_name = link.substr(dot_pos + 1);
|
||||
@ -3868,7 +3868,7 @@ void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_ta
|
||||
EditorHelpBitTooltip *tooltip = memnew(EditorHelpBitTooltip(p_target));
|
||||
p_help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free));
|
||||
tooltip->add_child(p_help_bit);
|
||||
p_target->get_viewport()->add_child(tooltip);
|
||||
p_target->add_child(tooltip);
|
||||
p_help_bit->update_content_height();
|
||||
tooltip->popup_under_cursor();
|
||||
}
|
||||
|
@ -2724,7 +2724,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn
|
||||
for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) {
|
||||
EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor);
|
||||
|
||||
if (ep && current_favorites.has(F.properties[0])) {
|
||||
if (ep && !F.properties.is_empty() && current_favorites.has(F.properties[0])) {
|
||||
ep->favorited = true;
|
||||
favorites_vbox->add_child(F.property_editor);
|
||||
} else {
|
||||
@ -3131,7 +3131,7 @@ void EditorInspector::update_tree() {
|
||||
|
||||
if (!array_prefix.is_empty()) {
|
||||
path = path.trim_prefix(array_prefix);
|
||||
int char_index = path.find("/");
|
||||
int char_index = path.find_char('/');
|
||||
if (char_index >= 0) {
|
||||
path = path.right(-char_index - 1);
|
||||
} else {
|
||||
@ -3171,10 +3171,10 @@ void EditorInspector::update_tree() {
|
||||
}
|
||||
|
||||
// Get the property label's string.
|
||||
String name_override = (path.contains("/")) ? path.substr(path.rfind("/") + 1) : path;
|
||||
String name_override = (path.contains("/")) ? path.substr(path.rfind_char('/') + 1) : path;
|
||||
String feature_tag;
|
||||
{
|
||||
const int dot = name_override.find(".");
|
||||
const int dot = name_override.find_char('.');
|
||||
if (dot != -1) {
|
||||
feature_tag = name_override.substr(dot);
|
||||
name_override = name_override.substr(0, dot);
|
||||
@ -3189,7 +3189,7 @@ void EditorInspector::update_tree() {
|
||||
const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style, p.name, doc_name) + feature_tag;
|
||||
|
||||
// Remove the property from the path.
|
||||
int idx = path.rfind("/");
|
||||
int idx = path.rfind_char('/');
|
||||
if (idx > -1) {
|
||||
path = path.left(idx);
|
||||
} else {
|
||||
@ -3320,7 +3320,7 @@ void EditorInspector::update_tree() {
|
||||
array_element_prefix = class_name_components[0];
|
||||
editor_inspector_array = memnew(EditorInspectorArray(all_read_only));
|
||||
|
||||
String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path;
|
||||
String array_label = path.contains("/") ? path.substr(path.rfind_char('/') + 1) : path;
|
||||
array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style, p.name, doc_name);
|
||||
int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0;
|
||||
editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding);
|
||||
|
@ -253,8 +253,8 @@ void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vecto
|
||||
// append that to yield "folder/foo.tscn".
|
||||
if (difference > 0) {
|
||||
String parent = full_path.substr(0, difference);
|
||||
int slash_idx = parent.rfind("/");
|
||||
slash_idx = parent.rfind("/", slash_idx - 1);
|
||||
int slash_idx = parent.rfind_char('/');
|
||||
slash_idx = parent.rfind_char('/', slash_idx - 1);
|
||||
parent = (slash_idx >= 0 && parent.length() > 1) ? parent.substr(slash_idx + 1) : parent;
|
||||
r_filenames.write[set_idx] = parent + r_filenames[set_idx];
|
||||
}
|
||||
|
@ -463,11 +463,13 @@ void EditorPropertyPath::_set_read_only(bool p_read_only) {
|
||||
|
||||
void EditorPropertyPath::_path_selected(const String &p_path) {
|
||||
String full_path = p_path;
|
||||
ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);
|
||||
|
||||
if (!global) {
|
||||
const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);
|
||||
if (id != ResourceUID::INVALID_ID) {
|
||||
full_path = ResourceUID::get_singleton()->id_to_text(id);
|
||||
}
|
||||
}
|
||||
|
||||
emit_changed(get_edited_property(), full_path);
|
||||
update_property();
|
||||
@ -476,7 +478,7 @@ void EditorPropertyPath::_path_selected(const String &p_path) {
|
||||
String EditorPropertyPath::_get_path_text() {
|
||||
String full_path = get_edited_property_value();
|
||||
if (full_path.begins_with("uid://")) {
|
||||
full_path = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(full_path));
|
||||
full_path = ResourceUID::uid_to_path(full_path);
|
||||
}
|
||||
|
||||
return full_path;
|
||||
|
@ -740,10 +740,10 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint
|
||||
// The format of p_hint_string is:
|
||||
// subType/subTypeHint:nextSubtype ... etc.
|
||||
if (!p_hint_string.is_empty()) {
|
||||
int hint_subtype_separator = p_hint_string.find(":");
|
||||
int hint_subtype_separator = p_hint_string.find_char(':');
|
||||
if (hint_subtype_separator >= 0) {
|
||||
String subtype_string = p_hint_string.substr(0, hint_subtype_separator);
|
||||
int slash_pos = subtype_string.find("/");
|
||||
int slash_pos = subtype_string.find_char('/');
|
||||
if (slash_pos >= 0) {
|
||||
subtype_hint = PropertyHint(subtype_string.substr(slash_pos + 1, subtype_string.size() - slash_pos - 1).to_int());
|
||||
subtype_string = subtype_string.substr(0, slash_pos);
|
||||
@ -1006,10 +1006,10 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s
|
||||
PackedStringArray types = p_hint_string.split(";");
|
||||
if (types.size() > 0 && !types[0].is_empty()) {
|
||||
String key = types[0];
|
||||
int hint_key_subtype_separator = key.find(":");
|
||||
int hint_key_subtype_separator = key.find_char(':');
|
||||
if (hint_key_subtype_separator >= 0) {
|
||||
String key_subtype_string = key.substr(0, hint_key_subtype_separator);
|
||||
int slash_pos = key_subtype_string.find("/");
|
||||
int slash_pos = key_subtype_string.find_char('/');
|
||||
if (slash_pos >= 0) {
|
||||
key_subtype_hint = PropertyHint(key_subtype_string.substr(slash_pos + 1, key_subtype_string.size() - slash_pos - 1).to_int());
|
||||
key_subtype_string = key_subtype_string.substr(0, slash_pos);
|
||||
@ -1025,10 +1025,10 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s
|
||||
}
|
||||
if (types.size() > 1 && !types[1].is_empty()) {
|
||||
String value = types[1];
|
||||
int hint_value_subtype_separator = value.find(":");
|
||||
int hint_value_subtype_separator = value.find_char(':');
|
||||
if (hint_value_subtype_separator >= 0) {
|
||||
String value_subtype_string = value.substr(0, hint_value_subtype_separator);
|
||||
int slash_pos = value_subtype_string.find("/");
|
||||
int slash_pos = value_subtype_string.find_char('/');
|
||||
if (slash_pos >= 0) {
|
||||
value_subtype_hint = PropertyHint(value_subtype_string.substr(slash_pos + 1, value_subtype_string.size() - slash_pos - 1).to_int());
|
||||
value_subtype_string = value_subtype_string.substr(0, slash_pos);
|
||||
@ -1173,6 +1173,11 @@ void EditorPropertyDictionary::update_property() {
|
||||
new_prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
new_prop->set_read_only(is_read_only());
|
||||
slot.set_prop(new_prop);
|
||||
} else if (slot.index != EditorPropertyDictionaryObject::NEW_KEY_INDEX && slot.index != EditorPropertyDictionaryObject::NEW_VALUE_INDEX) {
|
||||
Variant key = dict.get_key_at_index(slot.index);
|
||||
String cs = key.get_construct_string();
|
||||
slot.prop->set_label(cs);
|
||||
slot.prop->set_tooltip_text(cs);
|
||||
}
|
||||
|
||||
// We need to grab the focus of the property that is being changed, even if the type didn't actually changed.
|
||||
@ -1200,7 +1205,8 @@ void EditorPropertyDictionary::update_property() {
|
||||
|
||||
void EditorPropertyDictionary::_remove_pressed(int p_slot_index) {
|
||||
Dictionary dict = object->get_dict().duplicate();
|
||||
dict.erase(dict.get_key_at_index(p_slot_index));
|
||||
int index = slots[p_slot_index].index;
|
||||
dict.erase(dict.get_key_at_index(index));
|
||||
|
||||
emit_changed(get_edited_property(), dict);
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ class SectionedInspectorFilter : public Object {
|
||||
List<PropertyInfo> pinfo;
|
||||
edited->get_property_list(&pinfo);
|
||||
for (PropertyInfo &pi : pinfo) {
|
||||
int sp = pi.name.find("/");
|
||||
int sp = pi.name.find_char('/');
|
||||
|
||||
if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/") || pi.name.begins_with("_global_script")) { //skip resource stuff
|
||||
continue;
|
||||
@ -255,7 +255,7 @@ void SectionedInspector::update_category_list() {
|
||||
continue;
|
||||
}
|
||||
|
||||
int sp = pi.name.find("/");
|
||||
int sp = pi.name.find_char('/');
|
||||
if (sp == -1) {
|
||||
pi.name = "global/" + pi.name;
|
||||
}
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "editor_translation_parser.h"
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
|
||||
@ -65,6 +64,21 @@ Error EditorTranslationParserPlugin::parse_file(const String &p_path, Vector<Str
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTranslationParserPlugin::get_comments(Vector<String> *r_ids_comment, Vector<String> *r_ids_ctx_plural_comment) {
|
||||
TypedArray<String> ids_comment;
|
||||
TypedArray<String> ids_ctx_plural_comment;
|
||||
|
||||
if (GDVIRTUAL_CALL(_get_comments, ids_comment, ids_ctx_plural_comment)) {
|
||||
for (int i = 0; i < ids_comment.size(); i++) {
|
||||
r_ids_comment->append(ids_comment[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ids_ctx_plural_comment.size(); i++) {
|
||||
r_ids_ctx_plural_comment->append(ids_ctx_plural_comment[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_extensions) const {
|
||||
Vector<String> extensions;
|
||||
if (GDVIRTUAL_CALL(_get_recognized_extensions, extensions)) {
|
||||
@ -78,6 +92,7 @@ void EditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_ex
|
||||
|
||||
void EditorTranslationParserPlugin::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_parse_file, "path", "msgids", "msgids_context_plural");
|
||||
GDVIRTUAL_BIND(_get_comments, "msgids_comment", "msgids_context_plural_comment");
|
||||
GDVIRTUAL_BIND(_get_recognized_extensions);
|
||||
}
|
||||
|
||||
|
@ -43,10 +43,12 @@ protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL3(_parse_file, String, TypedArray<String>, TypedArray<Array>)
|
||||
GDVIRTUAL2(_get_comments, TypedArray<String>, TypedArray<String>)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions)
|
||||
|
||||
public:
|
||||
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural);
|
||||
virtual void get_comments(Vector<String> *r_ids_comment, Vector<String> *r_ids_ctx_plural_comment);
|
||||
virtual void get_recognized_extensions(List<String> *r_extensions) const;
|
||||
};
|
||||
|
||||
|
@ -240,8 +240,8 @@ EngineUpdateLabel::VersionType EngineUpdateLabel::_get_version_type(const String
|
||||
}
|
||||
|
||||
String EngineUpdateLabel::_extract_sub_string(const String &p_line) const {
|
||||
int j = p_line.find("\"") + 1;
|
||||
return p_line.substr(j, p_line.find("\"", j) - j);
|
||||
int j = p_line.find_char('"') + 1;
|
||||
return p_line.substr(j, p_line.find_char('"', j) - j);
|
||||
}
|
||||
|
||||
void EngineUpdateLabel::_notification(int p_what) {
|
||||
|
@ -121,7 +121,7 @@ String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, boo
|
||||
}
|
||||
|
||||
String EventListenerLineEdit::get_device_string(int p_device) {
|
||||
if (p_device == InputEvent::DEVICE_ID_ALL_DEVICES) {
|
||||
if (p_device == InputMap::ALL_DEVICES) {
|
||||
return TTR("All Devices");
|
||||
}
|
||||
return TTR("Device") + " " + itos(p_device);
|
||||
|
@ -1206,6 +1206,9 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
|
||||
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
|
||||
bool export_as_patch = fd_option.get(TTR("Export As Patch"), true);
|
||||
|
||||
EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug);
|
||||
EditorSettings::get_singleton()->set_project_metadata("export_options", "export_as_patch", export_as_patch);
|
||||
|
||||
if (p_path.ends_with(".zip")) {
|
||||
if (export_as_patch) {
|
||||
platform->export_zip_patch(current, export_debug, p_path);
|
||||
@ -1305,6 +1308,8 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) {
|
||||
Dictionary fd_option = export_project->get_selected_options();
|
||||
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
|
||||
|
||||
EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug);
|
||||
|
||||
Error err = platform->export_project(current, export_debug, current->get_export_path(), 0);
|
||||
result_dialog_log->clear();
|
||||
if (err != ERR_SKIP) {
|
||||
@ -1774,9 +1779,9 @@ ProjectExportDialog::ProjectExportDialog() {
|
||||
export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path));
|
||||
export_project->get_line_edit()->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_validate_export_path));
|
||||
|
||||
export_project->add_option(TTR("Export With Debug"), Vector<String>(), true);
|
||||
export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), true);
|
||||
export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), true);
|
||||
export_project->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true));
|
||||
export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true));
|
||||
export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_as_patch", true));
|
||||
|
||||
set_hide_on_ok(false);
|
||||
|
||||
|
@ -137,7 +137,7 @@ bool FileSystemList::edit_selected() {
|
||||
|
||||
String name = get_item_text(s);
|
||||
line_editor->set_text(name);
|
||||
line_editor->select(0, name.rfind("."));
|
||||
line_editor->select(0, name.rfind_char('.'));
|
||||
|
||||
popup_edit_commited = false; // Start edit popup processing.
|
||||
popup_editor->popup();
|
||||
@ -2432,7 +2432,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
|
||||
|
||||
if (to_rename.is_file) {
|
||||
String name = to_rename.path.get_file();
|
||||
tree->set_editor_selection(0, name.rfind("."));
|
||||
tree->set_editor_selection(0, name.rfind_char('.'));
|
||||
} else {
|
||||
String name = to_rename.path.left(-1).get_file(); // Removes the "/" suffix for folders.
|
||||
tree->set_editor_selection(0, name.length());
|
||||
@ -3658,10 +3658,10 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) {
|
||||
tree_item->select(0);
|
||||
} else {
|
||||
// Find parent folder.
|
||||
fpath = fpath.substr(0, fpath.rfind("/") + 1);
|
||||
fpath = fpath.substr(0, fpath.rfind_char('/') + 1);
|
||||
if (fpath.size() > String("res://").size()) {
|
||||
fpath = fpath.left(fpath.size() - 2); // Remove last '/'.
|
||||
const int slash_idx = fpath.rfind("/");
|
||||
const int slash_idx = fpath.rfind_char('/');
|
||||
fpath = fpath.substr(slash_idx + 1, fpath.size() - slash_idx - 1);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ void EditorFileDialog::_native_popup() {
|
||||
} else if (access == ACCESS_USERDATA) {
|
||||
root = OS::get_singleton()->get_user_data_dir();
|
||||
}
|
||||
DisplayServer::get_singleton()->file_dialog_with_options_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb));
|
||||
DisplayServer::get_singleton()->file_dialog_with_options_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), processed_filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb));
|
||||
}
|
||||
|
||||
void EditorFileDialog::popup(const Rect2i &p_rect) {
|
||||
@ -156,7 +156,7 @@ void EditorFileDialog::popup_file_dialog() {
|
||||
}
|
||||
|
||||
void EditorFileDialog::_focus_file_text() {
|
||||
int lp = file->get_text().rfind(".");
|
||||
int lp = file->get_text().rfind_char('.');
|
||||
if (lp != -1) {
|
||||
file->select(0, lp);
|
||||
file->grab_focus();
|
||||
@ -1148,37 +1148,54 @@ void EditorFileDialog::_filter_selected(int) {
|
||||
|
||||
void EditorFileDialog::update_filters() {
|
||||
filter->clear();
|
||||
processed_filters.clear();
|
||||
|
||||
if (filters.size() > 1) {
|
||||
String all_filters;
|
||||
String all_filters_full;
|
||||
|
||||
const int max_filters = 5;
|
||||
|
||||
for (int i = 0; i < MIN(max_filters, filters.size()); i++) {
|
||||
String flt = filters[i].get_slice(";", 0).strip_edges();
|
||||
String flt = filters[i].get_slicec(';', 0).strip_edges();
|
||||
if (i > 0) {
|
||||
all_filters += ", ";
|
||||
}
|
||||
all_filters += flt;
|
||||
}
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slicec(';', 0).strip_edges();
|
||||
if (i > 0) {
|
||||
all_filters_full += ",";
|
||||
}
|
||||
all_filters_full += flt;
|
||||
}
|
||||
|
||||
if (max_filters < filters.size()) {
|
||||
all_filters += ", ...";
|
||||
}
|
||||
|
||||
filter->add_item(TTR("All Recognized") + " (" + all_filters + ")");
|
||||
String f = TTR("All Recognized") + " (" + all_filters + ")";
|
||||
filter->add_item(f);
|
||||
processed_filters.push_back(all_filters_full + ";" + f);
|
||||
}
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slice(";", 0).strip_edges();
|
||||
String flt = filters[i].get_slicec(';', 0).strip_edges();
|
||||
String desc = filters[i].get_slice(";", 1).strip_edges();
|
||||
if (desc.length()) {
|
||||
filter->add_item(desc + " (" + flt + ")");
|
||||
String f = desc + " (" + flt + ")";
|
||||
filter->add_item(f);
|
||||
processed_filters.push_back(flt + ";" + f);
|
||||
} else {
|
||||
filter->add_item("(" + flt + ")");
|
||||
String f = "(" + flt + ")";
|
||||
filter->add_item(f);
|
||||
processed_filters.push_back(flt + ";" + f);
|
||||
}
|
||||
}
|
||||
|
||||
filter->add_item(TTR("All Files (*)"));
|
||||
String f = TTR("All Files (*)");
|
||||
filter->add_item(f);
|
||||
processed_filters.push_back("*.*;" + f);
|
||||
}
|
||||
|
||||
void EditorFileDialog::clear_filters() {
|
||||
@ -1246,7 +1263,7 @@ void EditorFileDialog::set_current_path(const String &p_path) {
|
||||
if (!p_path.size()) {
|
||||
return;
|
||||
}
|
||||
int pos = MAX(p_path.rfind("/"), p_path.rfind("\\"));
|
||||
int pos = MAX(p_path.rfind_char('/'), p_path.rfind_char('\\'));
|
||||
if (pos == -1) {
|
||||
set_current_file(p_path);
|
||||
} else {
|
||||
|
@ -145,6 +145,7 @@ private:
|
||||
void _push_history();
|
||||
|
||||
Vector<String> filters;
|
||||
Vector<String> processed_filters;
|
||||
|
||||
bool previews_enabled = true;
|
||||
bool preview_waiting = false;
|
||||
|
@ -67,6 +67,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
|
||||
} else {
|
||||
set_value(get_value() - get_step());
|
||||
}
|
||||
emit_signal("updown_pressed");
|
||||
return;
|
||||
}
|
||||
_grab_start();
|
||||
@ -696,6 +697,7 @@ void EditorSpinSlider::_bind_methods() {
|
||||
|
||||
ADD_SIGNAL(MethodInfo("grabbed"));
|
||||
ADD_SIGNAL(MethodInfo("ungrabbed"));
|
||||
ADD_SIGNAL(MethodInfo("updown_pressed"));
|
||||
ADD_SIGNAL(MethodInfo("value_focus_entered"));
|
||||
ADD_SIGNAL(MethodInfo("value_focus_exited"));
|
||||
|
||||
|
@ -199,7 +199,7 @@ Error ResourceImporterImageFont::import(ResourceUID::ID p_source_id, const Strin
|
||||
case STEP_OFF_Y_BEGIN: {
|
||||
// Read advance and offset.
|
||||
if (range[c] == ' ') {
|
||||
int next = range.find(" ", c + 1);
|
||||
int next = range.find_char(' ', c + 1);
|
||||
if (next < c) {
|
||||
next = range.length();
|
||||
}
|
||||
|
@ -590,7 +590,7 @@ Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
const Color color = target_image->get_pixel(i, j);
|
||||
target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
|
||||
target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b, color.a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,18 +551,18 @@ void InputEventConfigurationDialog::_input_list_item_selected() {
|
||||
}
|
||||
|
||||
void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {
|
||||
// Option index 0 corresponds to "All Devices" (value of -3).
|
||||
// Otherwise subtract 1 as option index 1 corresponds to device 0, etc...
|
||||
event->set_device(p_option_button_index == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : p_option_button_index - 1);
|
||||
// Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
|
||||
// and option index 1 corresponds to device 0, etc...
|
||||
event->set_device(p_option_button_index - 1);
|
||||
event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
|
||||
}
|
||||
|
||||
void InputEventConfigurationDialog::_set_current_device(int p_device) {
|
||||
device_id_option->select(p_device == InputEvent::DEVICE_ID_ALL_DEVICES ? 0 : p_device + 1);
|
||||
device_id_option->select(p_device + 1);
|
||||
}
|
||||
|
||||
int InputEventConfigurationDialog::_get_current_device() const {
|
||||
return device_id_option->get_selected() == 0 ? InputEvent::DEVICE_ID_ALL_DEVICES : device_id_option->get_selected() - 1;
|
||||
return device_id_option->get_selected() - 1;
|
||||
}
|
||||
|
||||
void InputEventConfigurationDialog::_notification(int p_what) {
|
||||
@ -705,12 +705,11 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
|
||||
|
||||
device_id_option = memnew(OptionButton);
|
||||
device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
device_id_option->add_item(EventListenerLineEdit::get_device_string(InputEvent::DEVICE_ID_ALL_DEVICES));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int i = -1; i < 8; i++) {
|
||||
device_id_option->add_item(EventListenerLineEdit::get_device_string(i));
|
||||
}
|
||||
device_id_option->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));
|
||||
_set_current_device(InputEvent::DEVICE_ID_ALL_DEVICES);
|
||||
_set_current_device(InputMap::ALL_DEVICES);
|
||||
device_container->add_child(device_id_option);
|
||||
|
||||
device_container->hide();
|
||||
|
@ -441,7 +441,7 @@ void LocalizationEditor::_filesystem_files_moved(const String &p_old_file, const
|
||||
bool remapped_files_updated = false;
|
||||
|
||||
for (int j = 0; j < remapped_files.size(); j++) {
|
||||
int splitter_pos = remapped_files[j].rfind(":");
|
||||
int splitter_pos = remapped_files[j].rfind_char(':');
|
||||
String res_path = remapped_files[j].substr(0, splitter_pos);
|
||||
|
||||
if (res_path == p_old_file) {
|
||||
@ -482,7 +482,7 @@ void LocalizationEditor::_filesystem_file_removed(const String &p_file) {
|
||||
for (int i = 0; i < remap_keys.size() && !remaps_changed; i++) {
|
||||
PackedStringArray remapped_files = remaps[remap_keys[i]];
|
||||
for (int j = 0; j < remapped_files.size() && !remaps_changed; j++) {
|
||||
int splitter_pos = remapped_files[j].rfind(":");
|
||||
int splitter_pos = remapped_files[j].rfind_char(':');
|
||||
String res_path = remapped_files[j].substr(0, splitter_pos);
|
||||
remaps_changed = p_file == res_path;
|
||||
if (remaps_changed) {
|
||||
@ -567,7 +567,7 @@ void LocalizationEditor::update_translations() {
|
||||
PackedStringArray selected = remaps[keys[i]];
|
||||
for (int j = 0; j < selected.size(); j++) {
|
||||
const String &s2 = selected[j];
|
||||
int qp = s2.rfind(":");
|
||||
int qp = s2.rfind_char(':');
|
||||
String path = s2.substr(0, qp);
|
||||
String locale = s2.substr(qp + 1, s2.length());
|
||||
|
||||
|
@ -907,7 +907,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
if (headers[i].findn("ETag:") == 0) { // Save etag
|
||||
String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().path_join("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
|
||||
String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges();
|
||||
String new_etag = headers[i].substr(headers[i].find_char(':') + 1, headers[i].length()).strip_edges();
|
||||
Ref<FileAccess> file = FileAccess::open(cache_filename_base + ".etag", FileAccess::WRITE);
|
||||
if (file.is_valid()) {
|
||||
file->store_line(new_etag);
|
||||
|
@ -93,10 +93,14 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
|
||||
file_dialog->popup_file_dialog();
|
||||
} break;
|
||||
case LightmapGI::BAKE_ERROR_NO_MESHES: {
|
||||
EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
|
||||
EditorNode::get_singleton()->show_warning(
|
||||
TTR("No meshes with lightmapping support to bake. Make sure they contain UV2 data and their Global Illumination property is set to Static.") +
|
||||
String::utf8("\n\n• ") + TTR("To import a scene with lightmapping support, set Meshes > Light Baking to Static Lightmaps in the Import dock.") +
|
||||
String::utf8("\n• ") + TTR("To enable lightmapping support on a primitive mesh, edit the PrimitiveMesh resource in the inspector and check Add UV2.") +
|
||||
String::utf8("\n• ") + TTR("To enable lightmapping support on a CSG mesh, select the root CSG node and choose CSG > Bake Mesh Instance at the top of the 3D editor viewport.\nSelect the generated MeshInstance3D node and choose Mesh > Unwrap UV2 for Lightmap/AO at the top of the 3D editor viewport."));
|
||||
} break;
|
||||
case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
|
||||
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure the lightmap destination path is writable."));
|
||||
} break;
|
||||
case LightmapGI::BAKE_ERROR_NO_SCENE_ROOT: {
|
||||
EditorNode::get_singleton()->show_warning(TTR("No editor scene root found."));
|
||||
@ -108,7 +112,7 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Maximum texture size is too small for the lightmap images.\nWhile this can be fixed by increasing the maximum texture size, it is recommended you split the scene into more objects instead."));
|
||||
} break;
|
||||
case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes selected to bake have `lightmap_size_hint` value set high enough, and `texel_scale` value of LightmapGI is not too low."));
|
||||
EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes to bake have the Lightmap Size Hint property set high enough, and the LightmapGI's Texel Scale value is not too low."));
|
||||
} break;
|
||||
case LightmapGI::BAKE_ERROR_ATLAS_TOO_SMALL: {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Failed fitting a lightmap image into an atlas. This should never happen and should be reported."));
|
||||
|
@ -69,8 +69,10 @@ void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
}
|
||||
|
||||
float height = obstacle->get_height();
|
||||
Basis gbi = obstacle->get_global_basis().inverse();
|
||||
|
||||
const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle->get_global_rotation().y, obstacle->get_global_basis().get_scale().abs().maxf(0.001));
|
||||
const Basis gbi = obstacle->get_global_basis().inverse();
|
||||
const Basis safe_global_basis = gbi * safe_basis;
|
||||
const int vertex_count = vertices.size();
|
||||
|
||||
Vector<Vector3> lines_mesh_vertices;
|
||||
@ -83,21 +85,22 @@ void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Vector3 point = vertices[i];
|
||||
Vector3 next_point = vertices[(i + 1) % vertex_count];
|
||||
|
||||
Vector3 direction = next_point.direction_to(point);
|
||||
Vector3 direction = safe_basis.xform(next_point.direction_to(point));
|
||||
Vector3 arrow_dir = direction.cross(Vector3(0.0, 1.0, 0.0));
|
||||
Vector3 edge_middle = point + ((next_point - point) * 0.5);
|
||||
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(edge_middle);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(edge_middle + (arrow_dir * 0.5));
|
||||
// Ensure vector stays perpendicular even when scaled non-uniformly.
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle) + gbi.xform(arrow_dir) * 0.5;
|
||||
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(point);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(next_point);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(next_point);
|
||||
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(point.x, height, point.z));
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(next_point.x, height, next_point.z));
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z));
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(next_point.x, height, next_point.z));
|
||||
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(point);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = gbi.xform(Vector3(point.x, height, point.z));
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point);
|
||||
lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z));
|
||||
}
|
||||
|
||||
Vector<Vector2> polygon_2d_vertices;
|
||||
@ -138,7 +141,8 @@ int NavigationObstacle3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DG
|
||||
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
|
||||
ERR_FAIL_NULL_V(obstacle_node, -1);
|
||||
|
||||
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
|
||||
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
|
||||
const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position());
|
||||
const Vector<Vector3> &vertices = obstacle_node->get_vertices();
|
||||
|
||||
for (int idx = 0; idx < vertices.size(); ++idx) {
|
||||
@ -160,7 +164,8 @@ Vector<int> NavigationObstacle3DGizmoPlugin::subgizmos_intersect_frustum(const E
|
||||
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
|
||||
ERR_FAIL_NULL_V(obstacle_node, contained_points);
|
||||
|
||||
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
|
||||
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
|
||||
const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position());
|
||||
const Vector<Vector3> &vertices = obstacle_node->get_vertices();
|
||||
|
||||
for (int idx = 0; idx < vertices.size(); ++idx) {
|
||||
@ -188,9 +193,8 @@ Transform3D NavigationObstacle3DGizmoPlugin::get_subgizmo_transform(const Editor
|
||||
const Vector<Vector3> &vertices = obstacle_node->get_vertices();
|
||||
ERR_FAIL_INDEX_V(p_id, vertices.size(), Transform3D());
|
||||
|
||||
Basis gbi = obstacle_node->get_global_basis().inverse();
|
||||
|
||||
Transform3D subgizmo_transform = Transform3D(Basis(), gbi.xform(vertices[p_id]));
|
||||
const Basis safe_basis_inverse = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)).inverse();
|
||||
Transform3D subgizmo_transform = Transform3D(Basis(), safe_basis_inverse.xform(vertices[p_id]));
|
||||
return subgizmo_transform;
|
||||
}
|
||||
|
||||
@ -198,14 +202,13 @@ void NavigationObstacle3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DG
|
||||
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
|
||||
ERR_FAIL_NULL(obstacle_node);
|
||||
|
||||
Basis gb = obstacle_node->get_global_basis();
|
||||
|
||||
const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001));
|
||||
Vector3 new_vertex_pos = p_transform.origin;
|
||||
|
||||
Vector<Vector3> vertices = obstacle_node->get_vertices();
|
||||
ERR_FAIL_INDEX(p_id, vertices.size());
|
||||
|
||||
Vector3 vertex = gb.xform(new_vertex_pos);
|
||||
Vector3 vertex = safe_basis.xform(new_vertex_pos);
|
||||
vertex.y = 0.0;
|
||||
vertices.write[p_id] = vertex;
|
||||
|
||||
@ -216,14 +219,14 @@ void NavigationObstacle3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *
|
||||
NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d());
|
||||
ERR_FAIL_NULL(obstacle_node);
|
||||
|
||||
Basis gb = obstacle_node->get_global_basis();
|
||||
const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001));
|
||||
|
||||
Vector<Vector3> vertices = obstacle_node->get_vertices();
|
||||
Vector<Vector3> restore_vertices = vertices;
|
||||
|
||||
for (int i = 0; i < p_ids.size(); ++i) {
|
||||
const int idx = p_ids[i];
|
||||
Vector3 vertex = gb.xform(p_restore[i].origin);
|
||||
Vector3 vertex = safe_basis.xform(p_restore[i].origin);
|
||||
vertex.y = 0.0;
|
||||
restore_vertices.write[idx] = vertex;
|
||||
}
|
||||
@ -446,7 +449,8 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_inp
|
||||
Vector3 ray_from = p_camera->project_ray_origin(mouse_position);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(mouse_position);
|
||||
|
||||
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
|
||||
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
|
||||
const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position());
|
||||
Transform3D gi = gt.affine_inverse();
|
||||
Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin);
|
||||
|
||||
@ -666,7 +670,8 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_inp
|
||||
Vector3 ray_from = p_camera->project_ray_origin(mouse_position);
|
||||
Vector3 ray_dir = p_camera->project_ray_normal(mouse_position);
|
||||
|
||||
Transform3D gt = Transform3D(Basis(), obstacle_node->get_global_position());
|
||||
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
|
||||
const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position());
|
||||
Transform3D gi = gt.affine_inverse();
|
||||
Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin);
|
||||
|
||||
@ -762,7 +767,9 @@ void NavigationObstacle3DEditorPlugin::redraw() {
|
||||
|
||||
rs->mesh_add_surface_from_arrays(point_lines_mesh_rid, RS::PRIMITIVE_LINES, point_lines_mesh_array);
|
||||
rs->instance_set_surface_override_material(point_lines_instance_rid, 0, line_material->get_rid());
|
||||
rs->instance_set_transform(point_lines_instance_rid, Transform3D(Basis(), obstacle_node->get_global_position()));
|
||||
const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001);
|
||||
const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position());
|
||||
rs->instance_set_transform(point_lines_instance_rid, gt);
|
||||
|
||||
Array point_handle_mesh_array;
|
||||
point_handle_mesh_array.resize(Mesh::ARRAY_MAX);
|
||||
@ -787,7 +794,7 @@ void NavigationObstacle3DEditorPlugin::redraw() {
|
||||
|
||||
rs->mesh_add_surface_from_arrays(point_handle_mesh_rid, RS::PRIMITIVE_POINTS, point_handle_mesh_array);
|
||||
rs->instance_set_surface_override_material(point_handles_instance_rid, 0, handle_material->get_rid());
|
||||
rs->instance_set_transform(point_handles_instance_rid, Transform3D(Basis(), obstacle_node->get_global_position()));
|
||||
rs->instance_set_transform(point_handles_instance_rid, gt);
|
||||
}
|
||||
|
||||
NavigationObstacle3DEditorPlugin *NavigationObstacle3DEditorPlugin::singleton = nullptr;
|
||||
|
@ -277,8 +277,15 @@ void Path3DGizmo::redraw() {
|
||||
Ref<StandardMaterial3D> path_tilt_material = gizmo_plugin->get_material("path_tilt_material", this);
|
||||
Ref<StandardMaterial3D> path_tilt_muted_material = gizmo_plugin->get_material("path_tilt_muted_material", this);
|
||||
Ref<StandardMaterial3D> handles_material = gizmo_plugin->get_material("handles");
|
||||
Ref<StandardMaterial3D> first_pt_handle_material = gizmo_plugin->get_material("first_pt_handle");
|
||||
Ref<StandardMaterial3D> last_pt_handle_material = gizmo_plugin->get_material("last_pt_handle");
|
||||
Ref<StandardMaterial3D> closed_pt_handle_material = gizmo_plugin->get_material("closed_pt_handle");
|
||||
Ref<StandardMaterial3D> sec_handles_material = gizmo_plugin->get_material("sec_handles");
|
||||
|
||||
first_pt_handle_material->set_albedo(Color(0.2, 1.0, 0.0));
|
||||
last_pt_handle_material->set_albedo(Color(1.0, 0.2, 0.0));
|
||||
closed_pt_handle_material->set_albedo(Color(1.0, 0.8, 0.0));
|
||||
|
||||
Ref<Curve3D> c = path->get_curve();
|
||||
if (c.is_null()) {
|
||||
return;
|
||||
@ -369,7 +376,7 @@ void Path3DGizmo::redraw() {
|
||||
info.point_idx = idx;
|
||||
|
||||
// Collect in-handles except for the first point.
|
||||
if (idx > 0 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
|
||||
if (idx > (c->is_closed() ? -1 : 0) && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
|
||||
const Vector3 in = c->get_point_in(idx);
|
||||
|
||||
info.type = HandleType::HANDLE_TYPE_IN;
|
||||
@ -383,7 +390,7 @@ void Path3DGizmo::redraw() {
|
||||
}
|
||||
|
||||
// Collect out-handles except for the last point.
|
||||
if (idx < c->get_point_count() - 1 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
|
||||
if (idx < (c->is_closed() ? c->get_point_count() : c->get_point_count() - 1) && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
|
||||
const Vector3 out = c->get_point_out(idx);
|
||||
|
||||
info.type = HandleType::HANDLE_TYPE_OUT;
|
||||
@ -441,7 +448,42 @@ void Path3DGizmo::redraw() {
|
||||
}
|
||||
|
||||
if (!Path3DEditorPlugin::singleton->curve_edit->is_pressed() && primary_handle_points.size()) {
|
||||
add_handles(primary_handle_points, handles_material);
|
||||
// Need to define indices separately.
|
||||
// Point count.
|
||||
const int pc = primary_handle_points.size();
|
||||
Vector<int> idx;
|
||||
idx.resize(pc);
|
||||
int *idx_ptr = idx.ptrw();
|
||||
for (int j = 0; j < pc; j++) {
|
||||
idx_ptr[j] = j;
|
||||
}
|
||||
|
||||
// Initialize arrays for first point.
|
||||
PackedVector3Array first_pt_handle_point;
|
||||
Vector<int> first_pt_id;
|
||||
first_pt_handle_point.append(primary_handle_points[0]);
|
||||
first_pt_id.append(idx[0]);
|
||||
|
||||
// Initialize arrays and add handle for last point if needed.
|
||||
if (pc > 1) {
|
||||
PackedVector3Array last_pt_handle_point;
|
||||
Vector<int> last_pt_id;
|
||||
last_pt_handle_point.append(primary_handle_points[pc - 1]);
|
||||
last_pt_id.append(idx[pc - 1]);
|
||||
primary_handle_points.remove_at(pc - 1);
|
||||
idx.remove_at(pc - 1);
|
||||
add_handles(last_pt_handle_point, c->is_closed() ? handles_material : last_pt_handle_material, last_pt_id);
|
||||
}
|
||||
|
||||
// Add handle for first point.
|
||||
primary_handle_points.remove_at(0);
|
||||
idx.remove_at(0);
|
||||
add_handles(first_pt_handle_point, c->is_closed() ? closed_pt_handle_material : first_pt_handle_material, first_pt_id);
|
||||
|
||||
// Add handles for remaining intermediate points.
|
||||
if (!primary_handle_points.is_empty()) {
|
||||
add_handles(primary_handle_points, handles_material, idx);
|
||||
}
|
||||
}
|
||||
if (secondary_handle_points.size()) {
|
||||
add_handles(secondary_handle_points, sec_handles_material, collected_secondary_handle_ids, false, true);
|
||||
@ -469,7 +511,7 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path, float p_disk_size) {
|
||||
Path3DEditorPlugin::singleton->curve_edit_curve->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
|
||||
Path3DEditorPlugin::singleton->curve_create->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
|
||||
Path3DEditorPlugin::singleton->curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
|
||||
Path3DEditorPlugin::singleton->curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
|
||||
Path3DEditorPlugin::singleton->curve_closed->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw));
|
||||
}
|
||||
|
||||
EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
|
||||
@ -696,7 +738,7 @@ void Path3DEditorPlugin::_mode_changed(int p_mode) {
|
||||
Node3DEditor::get_singleton()->clear_subgizmo_selection();
|
||||
}
|
||||
|
||||
void Path3DEditorPlugin::_close_curve() {
|
||||
void Path3DEditorPlugin::_toggle_closed_curve() {
|
||||
Ref<Curve3D> c = path->get_curve();
|
||||
if (c.is_null()) {
|
||||
return;
|
||||
@ -704,13 +746,10 @@ void Path3DEditorPlugin::_close_curve() {
|
||||
if (c->get_point_count() < 2) {
|
||||
return;
|
||||
}
|
||||
if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
|
||||
return;
|
||||
}
|
||||
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
|
||||
ur->create_action(TTR("Close Curve"));
|
||||
ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
|
||||
ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
|
||||
ur->create_action(TTR("Toggle Open/Closed Curve"));
|
||||
ur->add_do_method(c.ptr(), "set_closed", !c.ptr()->is_closed());
|
||||
ur->add_undo_method(c.ptr(), "set_closed", c.ptr()->is_closed());
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
@ -771,6 +810,7 @@ void Path3DEditorPlugin::_clear_curve_points() {
|
||||
return;
|
||||
}
|
||||
Ref<Curve3D> curve = path->get_curve();
|
||||
curve->set_closed(false);
|
||||
curve->clear_points();
|
||||
}
|
||||
|
||||
@ -795,7 +835,7 @@ void Path3DEditorPlugin::_update_theme() {
|
||||
curve_edit_tilt->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveTilt")));
|
||||
curve_create->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCreate")));
|
||||
curve_del->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveDelete")));
|
||||
curve_close->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose")));
|
||||
curve_closed->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose")));
|
||||
curve_clear_points->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("Clear")));
|
||||
create_curve_button->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("Curve3D")));
|
||||
}
|
||||
@ -872,12 +912,12 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
|
||||
toolbar->add_child(curve_del);
|
||||
curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_DELETE));
|
||||
|
||||
curve_close = memnew(Button);
|
||||
curve_close->set_theme_type_variation("FlatButton");
|
||||
curve_close->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_close->set_tooltip_text(TTR("Close Curve"));
|
||||
toolbar->add_child(curve_close);
|
||||
curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_close_curve));
|
||||
curve_closed = memnew(Button);
|
||||
curve_closed->set_theme_type_variation("FlatButton");
|
||||
curve_closed->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_closed->set_tooltip_text(TTR("Close Curve"));
|
||||
toolbar->add_child(curve_closed);
|
||||
curve_closed->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_toggle_closed_curve));
|
||||
|
||||
curve_clear_points = memnew(Button);
|
||||
curve_clear_points->set_theme_type_variation("FlatButton");
|
||||
@ -943,6 +983,14 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
Ref<Curve3D> curve = path->get_curve();
|
||||
|
||||
Ref<StandardMaterial3D> handle_material = get_material("handles", p_gizmo);
|
||||
Ref<StandardMaterial3D> first_pt_handle_material = get_material("first_pt_handle", p_gizmo);
|
||||
Ref<StandardMaterial3D> last_pt_handle_material = get_material("last_pt_handle", p_gizmo);
|
||||
Ref<StandardMaterial3D> closed_pt_handle_material = get_material("closed_pt_handle", p_gizmo);
|
||||
|
||||
first_pt_handle_material->set_albedo(Color(0.2, 1.0, 0.0));
|
||||
last_pt_handle_material->set_albedo(Color(1.0, 0.2, 0.0));
|
||||
closed_pt_handle_material->set_albedo(Color(1.0, 0.8, 0.0));
|
||||
|
||||
PackedVector3Array handles;
|
||||
|
||||
if (Path3DEditorPlugin::singleton->curve_edit->is_pressed()) {
|
||||
@ -955,9 +1003,39 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
|
||||
}
|
||||
|
||||
if (handles.size()) {
|
||||
// Point count.
|
||||
const int pc = handles.size();
|
||||
|
||||
// Initialize arrays for first point.
|
||||
PackedVector3Array first_pt;
|
||||
first_pt.append(handles[0]);
|
||||
|
||||
// Initialize arrays and add handle for last point if needed.
|
||||
if (pc > 1) {
|
||||
PackedVector3Array last_pt;
|
||||
last_pt.append(handles[handles.size() - 1]);
|
||||
handles.remove_at(handles.size() - 1);
|
||||
if (curve->is_closed()) {
|
||||
p_gizmo->add_vertices(last_pt, handle_material, Mesh::PRIMITIVE_POINTS);
|
||||
} else {
|
||||
p_gizmo->add_vertices(last_pt, last_pt_handle_material, Mesh::PRIMITIVE_POINTS);
|
||||
}
|
||||
}
|
||||
|
||||
// Add handle for first point.
|
||||
handles.remove_at(0);
|
||||
if (curve->is_closed()) {
|
||||
p_gizmo->add_vertices(first_pt, closed_pt_handle_material, Mesh::PRIMITIVE_POINTS);
|
||||
} else {
|
||||
p_gizmo->add_vertices(first_pt, first_pt_handle_material, Mesh::PRIMITIVE_POINTS);
|
||||
}
|
||||
|
||||
// Add handles for remaining intermediate points.
|
||||
if (!handles.is_empty()) {
|
||||
p_gizmo->add_vertices(handles, handle_material, Mesh::PRIMITIVE_POINTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Path3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
|
||||
Path3D *path = Object::cast_to<Path3D>(p_gizmo->get_node_3d());
|
||||
@ -1072,5 +1150,8 @@ Path3DGizmoPlugin::Path3DGizmoPlugin(float p_disk_size) {
|
||||
create_material("path_tilt_material", path_tilt_color);
|
||||
create_material("path_tilt_muted_material", path_tilt_color * 0.7);
|
||||
create_handle_material("handles", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
|
||||
create_handle_material("first_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
|
||||
create_handle_material("last_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
|
||||
create_handle_material("closed_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons)));
|
||||
create_handle_material("sec_handles", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorCurveHandle"), EditorStringName(EditorIcons)));
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ class Path3DEditorPlugin : public EditorPlugin {
|
||||
Button *curve_edit_curve = nullptr;
|
||||
Button *curve_edit_tilt = nullptr;
|
||||
Button *curve_del = nullptr;
|
||||
Button *curve_close = nullptr;
|
||||
Button *curve_closed = nullptr;
|
||||
Button *curve_clear_points = nullptr;
|
||||
MenuButton *handle_menu = nullptr;
|
||||
|
||||
@ -144,7 +144,7 @@ class Path3DEditorPlugin : public EditorPlugin {
|
||||
void _update_toolbar();
|
||||
|
||||
void _mode_changed(int p_mode);
|
||||
void _close_curve();
|
||||
void _toggle_closed_curve();
|
||||
void _handle_option_pressed(int p_option);
|
||||
bool handle_clicked = false;
|
||||
bool mirror_handle_angle = true;
|
||||
|
@ -282,7 +282,7 @@ void ScriptTextEditor::_warning_clicked(const Variant &p_line) {
|
||||
CodeEdit *text_editor = code_editor->get_text_editor();
|
||||
String prev_line = line > 0 ? text_editor->get_line(line - 1) : "";
|
||||
if (prev_line.contains("@warning_ignore")) {
|
||||
const int closing_bracket_idx = prev_line.find(")");
|
||||
const int closing_bracket_idx = prev_line.find_char(')');
|
||||
const String text_to_insert = ", " + code.quote(quote_style);
|
||||
text_editor->insert_text(text_to_insert, line - 1, closing_bracket_idx);
|
||||
} else {
|
||||
@ -951,7 +951,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
|
||||
} else if (p_symbol.is_resource_file() || p_symbol.begins_with("uid://")) {
|
||||
String symbol = p_symbol;
|
||||
if (symbol.begins_with("uid://")) {
|
||||
symbol = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(symbol));
|
||||
symbol = ResourceUID::uid_to_path(symbol);
|
||||
}
|
||||
|
||||
List<String> scene_extensions;
|
||||
@ -1205,7 +1205,7 @@ void ScriptTextEditor::_update_connected_methods() {
|
||||
|
||||
// Account for inner classes by stripping the class names from the method,
|
||||
// starting from the right since our inner class might be inside of another inner class.
|
||||
int pos = raw_name.rfind(".");
|
||||
int pos = raw_name.rfind_char('.');
|
||||
if (pos != -1) {
|
||||
name = raw_name.substr(pos + 1);
|
||||
}
|
||||
|
@ -4125,6 +4125,7 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in
|
||||
|
||||
int from = p_from.to_int();
|
||||
int to = p_to.to_int();
|
||||
bool swap = last_to_node != -1 && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL);
|
||||
|
||||
if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) {
|
||||
return;
|
||||
@ -4142,6 +4143,14 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port);
|
||||
|
||||
if (swap) {
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port);
|
||||
undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port);
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4155,6 +4164,9 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in
|
||||
undo_redo->add_do_method(graph_plugin.ptr(), "update_node", (int)type, to);
|
||||
undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", (int)type, to);
|
||||
undo_redo->commit_action();
|
||||
|
||||
last_to_node = -1;
|
||||
last_to_port = -1;
|
||||
}
|
||||
|
||||
void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
|
||||
@ -4165,6 +4177,11 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from
|
||||
int from = p_from.to_int();
|
||||
int to = p_to.to_int();
|
||||
|
||||
last_to_node = to;
|
||||
last_to_port = p_to_index;
|
||||
|
||||
info_label->show();
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Nodes Disconnected"));
|
||||
undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index);
|
||||
@ -4176,6 +4193,10 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void VisualShaderEditor::_connection_drag_ended() {
|
||||
info_label->hide();
|
||||
}
|
||||
|
||||
void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) {
|
||||
from_node = p_from.to_int();
|
||||
from_slot = p_from_slot;
|
||||
@ -5080,8 +5101,11 @@ void VisualShaderEditor::_param_property_changed(const String &p_property, const
|
||||
void VisualShaderEditor::_update_current_param() {
|
||||
if (current_prop != nullptr) {
|
||||
String name = current_prop->get_meta("id");
|
||||
if (visual_shader->_has_preview_shader_parameter(name)) {
|
||||
preview_material->set("shader_parameter/" + name, visual_shader->_get_preview_shader_parameter(name));
|
||||
|
||||
} else {
|
||||
preview_material->set("shader_parameter/" + name, Variant());
|
||||
}
|
||||
current_prop->update_property();
|
||||
current_prop->update_editor_property_status();
|
||||
current_prop->update_cache();
|
||||
@ -6367,6 +6391,7 @@ VisualShaderEditor::VisualShaderEditor() {
|
||||
graph->connect(SceneStringName(gui_input), callable_mp(this, &VisualShaderEditor::_graph_gui_input));
|
||||
graph->connect("connection_to_empty", callable_mp(this, &VisualShaderEditor::_connection_to_empty));
|
||||
graph->connect("connection_from_empty", callable_mp(this, &VisualShaderEditor::_connection_from_empty));
|
||||
graph->connect("connection_drag_ended", callable_mp(this, &VisualShaderEditor::_connection_drag_ended));
|
||||
graph->connect(SceneStringName(visibility_changed), callable_mp(this, &VisualShaderEditor::_visibility_changed));
|
||||
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR);
|
||||
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR_INT);
|
||||
@ -6427,6 +6452,13 @@ VisualShaderEditor::VisualShaderEditor() {
|
||||
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM);
|
||||
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SAMPLER, VisualShaderNode::PORT_TYPE_SAMPLER);
|
||||
|
||||
info_label = memnew(Label);
|
||||
info_label->set_text(vformat(TTR("Hold %s Key To Swap Connections"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)));
|
||||
info_label->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_WIDE, PRESET_MODE_MINSIZE, 20);
|
||||
info_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
info_label->hide();
|
||||
graph->get_top_layer()->add_child(info_label);
|
||||
|
||||
PanelContainer *toolbar_panel = static_cast<PanelContainer *>(graph->get_menu_hbox()->get_parent());
|
||||
toolbar_panel->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE, PRESET_MODE_MINSIZE, 10);
|
||||
toolbar_panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
|
||||
|
@ -221,6 +221,10 @@ class VisualShaderEditor : public ShaderEditor {
|
||||
Button *code_preview_button = nullptr;
|
||||
Button *shader_preview_button = nullptr;
|
||||
|
||||
int last_to_node = -1;
|
||||
int last_to_port = -1;
|
||||
Label *info_label = nullptr;
|
||||
|
||||
OptionButton *edit_type = nullptr;
|
||||
OptionButton *edit_type_standard = nullptr;
|
||||
OptionButton *edit_type_particles = nullptr;
|
||||
@ -502,6 +506,7 @@ class VisualShaderEditor : public ShaderEditor {
|
||||
|
||||
void _unlink_node_from_parent_frame(int p_node_id);
|
||||
|
||||
void _connection_drag_ended();
|
||||
void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
|
||||
void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
|
||||
bool _check_node_drop_on_connection(const Vector2 &p_position, Ref<GraphEdit::Connection> *r_closest_connection, int *r_node_id = nullptr, int *r_to_port = nullptr);
|
||||
|
@ -69,11 +69,16 @@ void POTGenerator::generate_pot(const String &p_file) {
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
Vector<String> msgids;
|
||||
Vector<Vector<String>> msgids_context_plural;
|
||||
|
||||
Vector<String> msgids_comment;
|
||||
Vector<String> msgids_context_plural_comment;
|
||||
|
||||
const String &file_path = files[i];
|
||||
String file_extension = file_path.get_extension();
|
||||
|
||||
if (EditorTranslationParser::get_singleton()->can_parse(file_extension)) {
|
||||
EditorTranslationParser::get_singleton()->get_parser(file_extension)->parse_file(file_path, &msgids, &msgids_context_plural);
|
||||
EditorTranslationParser::get_singleton()->get_parser(file_extension)->get_comments(&msgids_comment, &msgids_context_plural_comment);
|
||||
} else {
|
||||
ERR_PRINT("Unrecognized file extension " + file_extension + " in generate_pot()");
|
||||
return;
|
||||
@ -81,16 +86,18 @@ void POTGenerator::generate_pot(const String &p_file) {
|
||||
|
||||
for (int j = 0; j < msgids_context_plural.size(); j++) {
|
||||
const Vector<String> &entry = msgids_context_plural[j];
|
||||
_add_new_msgid(entry[0], entry[1], entry[2], file_path);
|
||||
const String &comment = (j < msgids_context_plural_comment.size()) ? msgids_context_plural_comment[j] : String();
|
||||
_add_new_msgid(entry[0], entry[1], entry[2], file_path, comment);
|
||||
}
|
||||
for (int j = 0; j < msgids.size(); j++) {
|
||||
_add_new_msgid(msgids[j], "", "", file_path);
|
||||
const String &comment = (j < msgids_comment.size()) ? msgids_comment[j] : String();
|
||||
_add_new_msgid(msgids[j], "", "", file_path, comment);
|
||||
}
|
||||
}
|
||||
|
||||
if (GLOBAL_GET("internationalization/locale/translation_add_builtin_strings_to_pot")) {
|
||||
for (const Vector<String> &extractable_msgids : get_extractable_message_list()) {
|
||||
_add_new_msgid(extractable_msgids[0], extractable_msgids[1], extractable_msgids[2], "");
|
||||
_add_new_msgid(extractable_msgids[0], extractable_msgids[1], extractable_msgids[2], "", "");
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,16 +143,26 @@ void POTGenerator::_write_to_pot(const String &p_file) {
|
||||
String context = v_msgid_data[i].ctx;
|
||||
String plural = v_msgid_data[i].plural;
|
||||
const HashSet<String> &locations = v_msgid_data[i].locations;
|
||||
const HashSet<String> &comments = v_msgid_data[i].comments;
|
||||
|
||||
// Put the blank line at the start, to avoid a double at the end when closing the file.
|
||||
file->store_line("");
|
||||
|
||||
// Write comments.
|
||||
bool is_first_comment = true;
|
||||
for (const String &E : comments) {
|
||||
if (is_first_comment) {
|
||||
file->store_line("#. TRANSLATORS: " + E.replace("\n", "\n#. "));
|
||||
} else {
|
||||
file->store_line("#. " + E.replace("\n", "\n#. "));
|
||||
}
|
||||
is_first_comment = false;
|
||||
}
|
||||
|
||||
// Write file locations.
|
||||
for (const String &E : locations) {
|
||||
if (!E.is_empty()) {
|
||||
file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Write context.
|
||||
if (!context.is_empty()) {
|
||||
@ -199,7 +216,7 @@ void POTGenerator::_write_msgid(Ref<FileAccess> r_file, const String &p_id, bool
|
||||
}
|
||||
}
|
||||
|
||||
void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location) {
|
||||
void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location, const String &p_comment) {
|
||||
// Insert new location if msgid under same context exists already.
|
||||
if (all_translation_strings.has(p_msgid)) {
|
||||
Vector<MsgidData> &v_mdata = all_translation_strings[p_msgid];
|
||||
@ -208,18 +225,27 @@ void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context
|
||||
if (!v_mdata[i].plural.is_empty() && !p_plural.is_empty() && v_mdata[i].plural != p_plural) {
|
||||
WARN_PRINT("Redefinition of plural message (msgid_plural), under the same message (msgid) and context (msgctxt)");
|
||||
}
|
||||
if (!p_location.is_empty()) {
|
||||
v_mdata.write[i].locations.insert(p_location);
|
||||
}
|
||||
if (!p_comment.is_empty()) {
|
||||
v_mdata.write[i].comments.insert(p_comment);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new entry of msgid, context, plural and location - context and plural might be empty if the inserted msgid doesn't associated
|
||||
// context or plurals.
|
||||
// Add a new entry.
|
||||
MsgidData mdata;
|
||||
mdata.ctx = p_context;
|
||||
mdata.plural = p_plural;
|
||||
if (!p_location.is_empty()) {
|
||||
mdata.locations.insert(p_location);
|
||||
}
|
||||
if (!p_comment.is_empty()) {
|
||||
mdata.comments.insert(p_comment);
|
||||
}
|
||||
all_translation_strings[p_msgid].push_back(mdata);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user