Merge branch 'godotengine:master' into master

This commit is contained in:
Gabriel Ami 2021-11-10 19:12:34 +01:00 committed by GitHub
commit 658ba4c75a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
429 changed files with 8571 additions and 3957 deletions

View file

@ -18,13 +18,15 @@ jobs:
fail-fast: false
matrix:
include:
- name: Editor (target=release_debug, tools=yes, tests=yes)
cache-name: linux-editor
- name: Editor w/ Mono (target=release_debug, tools=yes, tests=yes)
cache-name: linux-editor-mono
target: release_debug
tools: true
tests: true
tests: false # Disabled due freeze caused by mix Mono build and CI
sconsflags: module_mono_enabled=yes mono_glue=no
doc-test: true
bin: "./bin/godot.linuxbsd.opt.tools.64"
bin: "./bin/godot.linuxbsd.opt.tools.64.mono"
build-mono: true
artifact: true
- name: Editor and sanitizers (target=debug, tools=yes, tests=yes, use_asan=yes, use_ubsan=yes)
@ -36,6 +38,7 @@ jobs:
proj-test: true
godot-cpp-test: true
bin: "./bin/godot.linuxbsd.tools.64s"
build-mono: false
# Skip 2GiB artifact speeding up action.
artifact: false
@ -45,6 +48,7 @@ jobs:
tools: false
tests: false
sconsflags: module_mono_enabled=yes mono_glue=no debug_symbols=no
build-mono: false
artifact: true
steps:
@ -80,11 +84,26 @@ jobs:
tools: ${{ matrix.tools }}
tests: ${{ matrix.tests }}
- name: Generate Mono glue
if: ${{ matrix.build-mono }}
run: |
${{ matrix.bin }} --headless --generate-mono-glue modules/mono/glue || true
# Rebuild with mono
- name: Compilation (mono_glue=yes)
uses: ./.github/actions/godot-build
if: ${{ matrix.build-mono }}
with:
sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }} mono_glue=yes
platform: linuxbsd
target: ${{ matrix.target }}
tools: ${{ matrix.tools }}
# Execute unit tests for the editor
- name: Unit tests
if: ${{ matrix.tests }}
run: |
${{ matrix.bin }} --test
${{ matrix.bin }} --test --headless
# Check class reference
- name: Check for class reference updates

View file

@ -475,7 +475,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *
}
// send_error will lock internally.
rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type, si);
rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si);
}
void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) {

View file

@ -33,7 +33,7 @@
#include "core/string/print_string.h"
#include "core/variant/variant.h"
real_t AABB::get_area() const {
real_t AABB::get_volume() const {
return size.x * size.y * size.z;
}

View file

@ -46,8 +46,8 @@ public:
Vector3 position;
Vector3 size;
real_t get_area() const; /// get area
_FORCE_INLINE_ bool has_no_area() const {
real_t get_volume() const;
_FORCE_INLINE_ bool has_no_volume() const {
return (size.x <= 0 || size.y <= 0 || size.z <= 0);
}

View file

@ -291,6 +291,19 @@ public:
return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
}
static _ALWAYS_INLINE_ float fract(float value) {
return value - floor(value);
}
static _ALWAYS_INLINE_ double fract(double value) {
return value - floor(value);
}
static _ALWAYS_INLINE_ float pingpong(float value, float length) {
return (length != 0.0f) ? abs(fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f;
}
static _ALWAYS_INLINE_ double pingpong(double value, double length) {
return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0;
}
// double only, as these functions are mainly used by the editor and not performance-critical,
static double ease(double p_x, double p_c);
static int step_decimals(double p_step);

View file

@ -86,13 +86,6 @@ public:
void operator*=(const Quaternion &p_q);
Quaternion operator*(const Quaternion &p_q) const;
Quaternion operator*(const Vector3 &v) const {
return Quaternion(w * v.x + y * v.z - z * v.y,
w * v.y + z * v.x - x * v.z,
w * v.z + x * v.y - y * v.x,
-x * v.x - y * v.y - z * v.z);
}
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
#ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized.");

View file

@ -73,6 +73,7 @@ class ThreadWorkPool {
ThreadData *threads = nullptr;
uint32_t thread_count = 0;
uint32_t threads_working = 0;
BaseWork *current_work = nullptr;
static void _thread_function(void *p_user);
@ -94,7 +95,9 @@ public:
current_work = w;
for (uint32_t i = 0; i < thread_count; i++) {
threads_working = MIN(p_elements, thread_count);
for (uint32_t i = 0; i < threads_working; i++) {
threads[i].work = w;
threads[i].start.post();
}
@ -117,19 +120,32 @@ public:
void end_work() {
ERR_FAIL_COND(current_work == nullptr);
for (uint32_t i = 0; i < thread_count; i++) {
for (uint32_t i = 0; i < threads_working; i++) {
threads[i].completed.wait();
threads[i].work = nullptr;
}
threads_working = 0;
memdelete(current_work);
current_work = nullptr;
}
template <class C, class M, class U>
void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
begin_work(p_elements, p_instance, p_method, p_userdata);
end_work();
switch (p_elements) {
case 0:
// Nothing to do, so do nothing.
break;
case 1:
// No value in pushing the work to another thread if it's a single job
// and we're going to wait for it to finish. Just run it right here.
(p_instance->*p_method)(0, p_userdata);
break;
default:
// Multiple jobs to do; commence threaded business.
begin_work(p_elements, p_instance, p_method, p_userdata);
end_work();
}
}
_FORCE_INLINE_ int get_thread_count() const { return thread_count; }

View file

@ -752,8 +752,9 @@ struct _VariantCall {
static PackedInt32Array func_PackedByteArray_decode_s32_array(PackedByteArray *p_instance) {
uint64_t size = p_instance->size();
const uint8_t *r = p_instance->ptr();
PackedInt32Array dest;
ERR_FAIL_COND_V_MSG(size < sizeof(int32_t), dest, "Size didn't match array of size int32_t, maybe you are trying to convert to the wrong type?");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(int32_t));
memcpy(dest.ptrw(), r, size);
return dest;
@ -761,8 +762,9 @@ struct _VariantCall {
static PackedInt64Array func_PackedByteArray_decode_s64_array(PackedByteArray *p_instance) {
uint64_t size = p_instance->size();
const uint8_t *r = p_instance->ptr();
PackedInt64Array dest;
ERR_FAIL_COND_V_MSG(size < sizeof(int64_t), dest, "Size didn't match array of size int64_t, maybe you are trying to convert to the wrong type?");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(int64_t));
memcpy(dest.ptrw(), r, size);
return dest;
@ -770,8 +772,9 @@ struct _VariantCall {
static PackedFloat32Array func_PackedByteArray_decode_float_array(PackedByteArray *p_instance) {
uint64_t size = p_instance->size();
const uint8_t *r = p_instance->ptr();
PackedFloat32Array dest;
ERR_FAIL_COND_V_MSG(size < sizeof(float), dest, "Size didn't match array of size float, maybe you are trying to convert to the wrong type?");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(float));
memcpy(dest.ptrw(), r, size);
return dest;
@ -779,8 +782,9 @@ struct _VariantCall {
static PackedFloat64Array func_PackedByteArray_decode_double_array(PackedByteArray *p_instance) {
uint64_t size = p_instance->size();
const uint8_t *r = p_instance->ptr();
PackedFloat64Array dest;
ERR_FAIL_COND_V_MSG(size < sizeof(double), dest, "Size didn't match array of size double, maybe you are trying to convert to the wrong type?");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(double));
memcpy(dest.ptrw(), r, size);
return dest;
@ -1748,8 +1752,8 @@ static void _register_variant_builtin_methods() {
bind_method(AABB, abs, sarray(), varray());
bind_method(AABB, get_center, sarray(), varray());
bind_method(AABB, get_area, sarray(), varray());
bind_method(AABB, has_no_area, sarray(), varray());
bind_method(AABB, get_volume, sarray(), varray());
bind_method(AABB, has_no_volume, sarray(), varray());
bind_method(AABB, has_no_surface, sarray(), varray());
bind_method(AABB, has_point, sarray("point"), varray());
bind_method(AABB, is_equal_approx, sarray("aabb"), varray());

View file

@ -275,6 +275,10 @@ struct VariantUtilityFunctions {
return Math::wrapf(value, min, max);
}
static inline double pingpong(double value, double length) {
return Math::pingpong(value, length);
}
static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
@ -1226,6 +1230,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(clampf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(nearest_po2, sarray("value"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(pingpong, sarray("value", "length"), Variant::UTILITY_FUNC_TYPE_MATH);
// Random

View file

@ -14,6 +14,26 @@
<return type="Variant" />
<argument index="0" name="x" type="Variant" />
<description>
Returns the absolute value of a [Variant] parameter [code]x[/code] (i.e. non-negative value). Variant types [int], [float] (real), [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported.
[codeblock]
var a = abs(-1)
# a is 1
var b = abs(-1.2)
# b is 1.2
var c = abs(Vector2(-3.5, -4))
# c is (3.5, 4)
var d = abs(Vector2i(-5, -6))
# d is (5, 6)
var e = abs(Vector3(-7, 8.5, -3.8))
# e is (7, 8.5, 3.8)
var f = abs(Vector3i(-7, -8, -9))
# f is (7, 8, 9)
[/codeblock]
</description>
</method>
<method name="absf">
@ -118,6 +138,26 @@
<argument index="1" name="min" type="Variant" />
<argument index="2" name="max" type="Variant" />
<description>
Clamps the [Variant] [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code]. Variant types [int], [float] (real), [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported.
[codeblock]
var a = clamp(-10, -1, 5)
# a is -1
var b = clamp(8.1, 0.9, 5.5)
# b is 5.5
var c = clamp(Vector2(-3.5, -4), Vector2(-3.2, -2), Vector2(2, 6.5))
# c is (-3.2, -2)
var d = clamp(Vector2i(7, 8), Vector2i(-3, -2), Vector2i(2, 6))
# d is (2, 6)
var e = clamp(Vector3(-7, 8.5, -3.8), Vector3(-3, -2, 5.4), Vector3(-2, 6, -4.1))
# e is (-3, -2, 5.4)
var f = clamp(Vector3i(-7, -8, -9), Vector3i(-1, 2, 3), Vector3i(-4, -5, -6))
# f is (-4, -5, -6)
[/codeblock]
</description>
</method>
<method name="clampf">
@ -347,6 +387,7 @@
<return type="bool" />
<argument index="0" name="id" type="int" />
<description>
Returns [code]true[/code] if the Object that corresponds to [code]instance_id[/code] is a valid object (e.g. has not been deleted from memory). All Objects have a unique instance ID.
</description>
</method>
<method name="is_instance_valid">
@ -525,6 +566,26 @@
[b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2).
</description>
</method>
<method name="pingpong">
<return type="float" />
<argument index="0" name="value" type="float" />
<argument index="1" name="length" type="float" />
<description>
Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
[codeblock]
pingpong(-3.0, 3.0) # Returns 3
pingpong(-2.0, 3.0) # Returns 2
pingpong(-1.0, 3.0) # Returns 1
pingpong(0.0, 3.0) # Returns 0
pingpong(1.0, 3.0) # Returns 1
pingpong(2.0, 3.0) # Returns 2
pingpong(3.0, 3.0) # Returns 3
pingpong(4.0, 3.0) # Returns 2
pingpong(5.0, 3.0) # Returns 1
pingpong(6.0, 3.0) # Returns 0
[/codeblock]
</description>
</method>
<method name="posmod">
<return type="int" />
<argument index="0" name="x" type="int" />
@ -750,6 +811,14 @@
<return type="Variant" />
<argument index="0" name="x" type="Variant" />
<description>
Returns the sign of [code]x[/code] as same type of [Variant] as [code]x[/code] with each component being -1, 0 and 1 for each negative, zero and positive values respectivelu. Variant types [int], [float] (real), [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported.
[codeblock]
sign(-6.0) # Returns -1
sign(0.0) # Returns 0
sign(6.0) # Returns 1
sign(Vector3(-6.0, 0.0, 6.0) # Returns (-1, 0, 1)
[/codeblock]
</description>
</method>
<method name="signf">

View file

@ -57,12 +57,6 @@
Returns this [AABB] expanded to include a given point.
</description>
</method>
<method name="get_area" qualifiers="const">
<return type="float" />
<description>
Returns the volume of the [AABB].
</description>
</method>
<method name="get_center" qualifiers="const">
<return type="Vector3" />
<description>
@ -119,6 +113,12 @@
Returns the support point in a given direction. This is useful for collision detection algorithms.
</description>
</method>
<method name="get_volume" qualifiers="const">
<return type="float" />
<description>
Returns the volume of the [AABB].
</description>
</method>
<method name="grow" qualifiers="const">
<return type="AABB" />
<argument index="0" name="by" type="float" />
@ -126,18 +126,18 @@
Returns a copy of the [AABB] grown a given amount of units towards all the sides.
</description>
</method>
<method name="has_no_area" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the [AABB] is flat or empty.
</description>
</method>
<method name="has_no_surface" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the [AABB] is empty.
</description>
</method>
<method name="has_no_volume" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the [AABB] is flat or empty.
</description>
</method>
<method name="has_point" qualifiers="const">
<return type="bool" />
<argument index="0" name="point" type="Vector3" />

View file

@ -551,8 +551,8 @@
The total length of the animation (in seconds).
[b]Note:[/b] Length is not delimited by the last key, as this one may be before or after the end to ensure correct interpolation and looping.
</member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
A flag indicating that the animation must loop. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation.
<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode" default="0">
Determines the behavior of both ends of the animation timeline during animation playback. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation.
</member>
<member name="step" type="float" setter="set_step" getter="get_step" default="0.1">
The animation step value.
@ -610,5 +610,14 @@
<constant name="UPDATE_CAPTURE" value="3" enum="UpdateMode">
Same as linear interpolation, but also interpolates from the current value (i.e. dynamically at runtime) if the first key isn't at 0 seconds.
</constant>
<constant name="LOOP_NONE" value="0" enum="LoopMode">
At both ends of the animation, the animation will stop playing.
</constant>
<constant name="LOOP_LINEAR" value="1" enum="LoopMode">
At both ends of the animation, the animation will be repeated without changing the playback direction.
</constant>
<constant name="LOOP_PINGPONG" value="2" enum="LoopMode">
Repeats playback and reverse playback at both ends of the animation.
</constant>
</constants>
</class>

View file

@ -73,6 +73,7 @@
<argument index="2" name="delta" type="float" />
<argument index="3" name="seeked" type="bool" />
<argument index="4" name="blend" type="float" />
<argument index="5" name="pingponged" type="int" default="0" />
<description>
Blend an animation by [code]blend[/code] amount (name must be valid in the linked [AnimationPlayer]). A [code]time[/code] and [code]delta[/code] may be passed, as well as whether [code]seek[/code] happened.
</description>

View file

@ -15,5 +15,14 @@
<member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&amp;&quot;&quot;">
Animation to use as an output. It is one of the animations provided by [member AnimationTree.anim_player].
</member>
<member name="play_mode" type="int" setter="set_play_mode" getter="get_play_mode" enum="AnimationNodeAnimation.PlayMode" default="0">
Determines the playback direction of the animation.
</member>
</members>
<constants>
<constant name="PLAY_MODE_FORWARD" value="0" enum="PlayMode">
</constant>
<constant name="PLAY_MODE_BACKWARD" value="1" enum="PlayMode">
</constant>
</constants>
</class>

View file

@ -61,7 +61,7 @@
<constant name="LOOP_FORWARD" value="1" enum="LoopMode">
Audio loops the data between [member loop_begin] and [member loop_end], playing forward only.
</constant>
<constant name="LOOP_PING_PONG" value="2" enum="LoopMode">
<constant name="LOOP_PINGPONG" value="2" enum="LoopMode">
Audio loops the data between [member loop_begin] and [member loop_end], playing back and forth.
</constant>
<constant name="LOOP_BACKWARD" value="3" enum="LoopMode">

View file

@ -48,7 +48,7 @@
<argument index="0" name="pixels" type="int" />
<argument index="1" name="rect" type="Rect2" />
<description>
Applies morphological dilation to the bitmap. The first argument is the dilation amount, Rect2 is the area where the dilation will be applied.
Applies morphological dilation or erosion to the bitmap. If [code]pixels[/code] is positive, dilation is applied to the bitmap. If [code]pixels[/code] is negative, erosion is applied to the bitmap. [code]rect[/code] defines the area where the morphological operation is applied. Pixels located outside the [code]rect[/code] are unaffected by [method grow_mask].
</description>
</method>
<method name="opaque_to_polygons" qualifiers="const">

View file

@ -126,7 +126,7 @@
Each particle's initial color. To have particle display color in a [BaseMaterial3D] make sure to set [member BaseMaterial3D.vertex_color_use_as_albedo] to [code]true[/code].
</member>
<member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
Each particle's color will vary along this [GradientTexture] over its lifetime (multiplied with [member color]).
Each particle's color will vary along this [GradientTexture1D] over its lifetime (multiplied with [member color]).
</member>
<member name="damping_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
Damping will vary along this [Curve].

View file

@ -110,7 +110,7 @@
</signal>
<signal name="property_checked">
<argument index="0" name="property" type="StringName" />
<argument index="1" name="bool" type="String" />
<argument index="1" name="checked" type="bool" />
<description>
Emitted when a property was checked. Used internally.
</description>
@ -134,6 +134,14 @@
Emit it if you want to key a property with a single value.
</description>
</signal>
<signal name="property_pinned">
<argument index="0" name="property" type="StringName" />
<argument index="1" name="pinned" type="bool" />
<description>
Emit it if you want to mark (or unmark) the value of a property for being saved regardless of being equal to the default value.
The default value is the one the property will get when the node is just instantiated and can come from an ancestor scene in the inheritance/instancing chain, a script or a builtin class.
</description>
</signal>
<signal name="resource_selected">
<argument index="0" name="path" type="String" />
<argument index="1" name="resource" type="Resource" />

View file

@ -39,7 +39,7 @@
The global brightness value of the rendered scene. Effective only if [code]adjustment_enabled[/code] is [code]true[/code].
</member>
<member name="adjustment_color_correction" type="Texture" setter="set_adjustment_color_correction" getter="get_adjustment_color_correction">
The [Texture2D] or [Texture3D] lookup table (LUT) to use for the built-in post-process color grading. Can use a [GradientTexture] for a 1-dimensional LUT, or a [Texture3D] for a more complex LUT. Effective only if [code]adjustment_enabled[/code] is [code]true[/code].
The [Texture2D] or [Texture3D] lookup table (LUT) to use for the built-in post-process color grading. Can use a [GradientTexture1D] for a 1-dimensional LUT, or a [Texture3D] for a more complex LUT. Effective only if [code]adjustment_enabled[/code] is [code]true[/code].
</member>
<member name="adjustment_contrast" type="float" setter="set_adjustment_contrast" getter="get_adjustment_contrast" default="1.0">
The global contrast value of the rendered scene (default value is 1). Effective only if [code]adjustment_enabled[/code] is [code]true[/code].

View file

@ -93,6 +93,24 @@
Returns font descent (number of pixels below the baseline).
</description>
</method>
<method name="get_font_name" qualifiers="const">
<return type="String" />
<description>
Returns font family name.
</description>
</method>
<method name="get_font_style" qualifiers="const">
<return type="int" />
<description>
Returns font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="get_font_style_name" qualifiers="const">
<return type="String" />
<description>
Returns font style name.
</description>
</method>
<method name="get_glyph_advance" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="cache_index" type="int" />
@ -463,6 +481,27 @@
Sets the font descent (number of pixels below the baseline).
</description>
</method>
<method name="set_font_name">
<return type="void" />
<argument index="0" name="name" type="String" />
<description>
Sets the font family name.
</description>
</method>
<method name="set_font_style">
<return type="void" />
<argument index="0" name="style" type="int" />
<description>
Sets the font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="set_font_style_name">
<return type="void" />
<argument index="0" name="name" type="String" />
<description>
Sets the font style name.
</description>
</method>
<method name="set_force_autohinter">
<return type="void" />
<argument index="0" name="force_autohinter" type="bool" />

View file

@ -51,6 +51,12 @@
Removes the color at the index [code]point[/code].
</description>
</method>
<method name="reverse">
<return type="void" />
<description>
Reverses/mirrors the gradient.
</description>
</method>
<method name="set_color">
<return type="void" />
<argument index="0" name="point" type="int" />
@ -72,8 +78,22 @@
<member name="colors" type="PackedColorArray" setter="set_colors" getter="get_colors" default="PackedColorArray(0, 0, 0, 1, 1, 1, 1, 1)">
Gradient's colors returned as a [PackedColorArray].
</member>
<member name="interpolation_mode" type="int" setter="set_interpolation_mode" getter="get_interpolation_mode" enum="Gradient.InterpolationMode" default="0">
Defines how the colors between points of the gradient are interpolated. See [enum InterpolationMode] for available modes.
</member>
<member name="offsets" type="PackedFloat32Array" setter="set_offsets" getter="get_offsets" default="PackedFloat32Array(0, 1)">
Gradient's offsets returned as a [PackedFloat32Array].
</member>
</members>
<constants>
<constant name="GRADIENT_INTERPOLATE_LINEAR" value="0" enum="InterpolationMode">
Linear interpolation.
</constant>
<constant name="GRADIENT_INTERPOLATE_CONSTANT" value="1" enum="InterpolationMode">
Constant interpolation, color changes abruptly at each point and stays uniform between. This might cause visible aliasing when used for a gradient texture in some cases.
</constant>
<constant name="GRADIENT_INTERPOLATE_CUBIC" value="2" enum="InterpolationMode">
Cubic interpolation.
</constant>
</constants>
</class>

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GradientTexture" inherits="Texture2D" version="4.0">
<class name="GradientTexture1D" inherits="Texture2D" version="4.0">
<brief_description>
Gradient-filled texture.
</brief_description>
<description>
GradientTexture uses a [Gradient] to fill the texture data. The gradient will be filled from left to right using colors obtained from the gradient. This means the texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width]).
GradientTexture1D uses a [Gradient] to fill the texture data. The gradient will be filled from left to right using colors obtained from the gradient. This means the texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [member width]).
</description>
<tutorials>
</tutorials>

View file

@ -194,7 +194,7 @@
The vertical offset of the text's shadow.
</theme_item>
<theme_item name="shadow_outline_size" data_type="constant" type="int" default="1">
Shadow outline size. If set to 1 or greater, the shadow will be displayed around the whole text as an outline.
The size of the shadow outline.
</theme_item>
</theme_items>
</class>

View file

@ -196,6 +196,9 @@
<member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length_enabled" getter="is_expand_to_text_length_enabled" default="false">
If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened.
</member>
<member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
If [code]true[/code], the [LineEdit] don't display decoration.
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" />
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.

View file

@ -121,5 +121,9 @@
If passed to [method instantiate], provides local scene resources to the local scene. Only the main scene should receive the main edit state.
[b]Note:[/b] Only available in editor builds.
</constant>
<constant name="GEN_EDIT_STATE_MAIN_INHERITED" value="3" enum="GenEditState">
It's similar to [constant GEN_EDIT_STATE_MAIN], but for the case where the scene is being instantiated to be the base of another one.
[b]Note:[/b] Only available in editor builds.
</constant>
</constants>
</class>

View file

@ -128,7 +128,7 @@
Each particle's initial color. If the [GPUParticles2D]'s [code]texture[/code] is defined, it will be multiplied by this color. To have particle display color in a [BaseMaterial3D] make sure to set [member BaseMaterial3D.vertex_color_use_as_albedo] to [code]true[/code].
</member>
<member name="color_ramp" type="Texture2D" setter="set_color_ramp" getter="get_color_ramp">
Each particle's color will vary along this [GradientTexture] over its lifetime (multiplied with [member color]).
Each particle's color will vary along this [GradientTexture1D] over its lifetime (multiplied with [member color]).
</member>
<member name="damping_curve" type="Texture2D" setter="set_param_texture" getter="get_param_texture">
Damping will vary along this [CurveTexture].

View file

@ -150,7 +150,10 @@
The body's rotational velocity.
</member>
<member name="center_of_mass" type="Vector2" setter="" getter="get_center_of_mass">
The body's center of mass.
The body's center of mass position relative to the body's center in the global coordinate system.
</member>
<member name="center_of_mass_local" type="Vector2" setter="" getter="get_center_of_mass_local">
The body's center of mass position in the body's local coordinate system.
</member>
<member name="inverse_inertia" type="float" setter="" getter="get_inverse_inertia">
The inverse of the inertia of the body.

View file

@ -159,7 +159,10 @@
The body's rotational velocity.
</member>
<member name="center_of_mass" type="Vector3" setter="" getter="get_center_of_mass">
The body's center of mass.
The body's center of mass position relative to the body's center in the global coordinate system.
</member>
<member name="center_of_mass_local" type="Vector3" setter="" getter="get_center_of_mass_local">
The body's center of mass position in the body's local coordinate system.
</member>
<member name="inverse_inertia" type="Vector3" setter="" getter="get_inverse_inertia">
The inverse of the inertia of the body.

View file

@ -13,7 +13,7 @@
<methods>
<method name="cast_motion">
<return type="Array" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters2D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters2D" />
<description>
Checks how far a [Shape2D] can move without colliding. All the parameters for the query, including the shape and the motion, are supplied through a [PhysicsShapeQueryParameters2D] object.
Returns an array with the safe and unsafe proportions (between 0 and 1) of the motion. The safe proportion is the maximum fraction of the motion that can be made without a collision. The unsafe proportion is the minimum fraction of the distance that must be moved for a collision. If no collision is detected a result of [code][1.0, 1.0][/code] will be returned.
@ -22,7 +22,7 @@
</method>
<method name="collide_shape">
<return type="Array" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters2D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters2D" />
<argument index="1" name="max_results" type="int" default="32" />
<description>
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters2D] object, against the space. The resulting array contains a list of points where the shape intersects another. Like with [method intersect_shape], the number of returned results can be limited to save processing time.
@ -31,7 +31,7 @@
</method>
<method name="get_rest_info">
<return type="Dictionary" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters2D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters2D" />
<description>
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters2D] object, against the space. If it collides with more than one shape, the nearest one is selected. If the shape did not intersect anything, then an empty dictionary is returned instead.
[b]Note:[/b] This method does not take into account the [code]motion[/code] property of the object. The returned object is a dictionary containing the following fields:
@ -45,51 +45,23 @@
</method>
<method name="intersect_point">
<return type="Array" />
<argument index="0" name="point" type="Vector2" />
<argument index="0" name="parameters" type="PhysicsPointQueryParameters2D" />
<argument index="1" name="max_results" type="int" default="32" />
<argument index="2" name="exclude" type="Array" default="[]" />
<argument index="3" name="collision_mask" type="int" default="4294967295" />
<argument index="4" name="collide_with_bodies" type="bool" default="true" />
<argument index="5" name="collide_with_areas" type="bool" default="false" />
<description>
Checks whether a point is inside any solid shape. The shapes the point is inside of are returned in an array containing dictionaries with the following fields:
Checks whether a point is inside any solid shape. Position and other parameters are defined through [PhysicsPointQueryParameters2D]. The shapes the point is inside of are returned in an array containing dictionaries with the following fields:
[code]collider[/code]: The colliding object.
[code]collider_id[/code]: The colliding object's ID.
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect (all layers by default), or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively.
[b]Note:[/b] [ConcavePolygonShape2D]s and [CollisionPolygon2D]s in [code]Segments[/code] build mode are not solid shapes. Therefore, they will not be detected.
</description>
</method>
<method name="intersect_point_on_canvas">
<return type="Array" />
<argument index="0" name="point" type="Vector2" />
<argument index="1" name="canvas_instance_id" type="int" />
<argument index="2" name="max_results" type="int" default="32" />
<argument index="3" name="exclude" type="Array" default="[]" />
<argument index="4" name="collision_mask" type="int" default="4294967295" />
<argument index="5" name="collide_with_bodies" type="bool" default="true" />
<argument index="6" name="collide_with_areas" type="bool" default="false" />
<description>
Checks whether a point is inside any solid shape, in a specific canvas layer given by [code]canvas_instance_id[/code]. The shapes the point is inside of are returned in an array containing dictionaries with the following fields:
[code]collider[/code]: The colliding object.
[code]collider_id[/code]: The colliding object's ID.
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect (all layers by default), or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively.
The number of intersections can be limited with the [code]max_results[/code] parameter, to reduce the processing time.
[b]Note:[/b] [ConcavePolygonShape2D]s and [CollisionPolygon2D]s in [code]Segments[/code] build mode are not solid shapes. Therefore, they will not be detected.
</description>
</method>
<method name="intersect_ray">
<return type="Dictionary" />
<argument index="0" name="from" type="Vector2" />
<argument index="1" name="to" type="Vector2" />
<argument index="2" name="exclude" type="Array" default="[]" />
<argument index="3" name="collision_mask" type="int" default="4294967295" />
<argument index="4" name="collide_with_bodies" type="bool" default="true" />
<argument index="5" name="collide_with_areas" type="bool" default="false" />
<argument index="0" name="parameters" type="PhysicsRayQueryParameters2D" />
<description>
Intersects a ray in a given space. The returned object is a dictionary with the following fields:
Intersects a ray in a given space. Ray position and other parameters are defined through [PhysicsRayQueryParameters2D]. The returned object is a dictionary with the following fields:
[code]collider[/code]: The colliding object.
[code]collider_id[/code]: The colliding object's ID.
[code]normal[/code]: The object's surface normal at the intersection point.
@ -97,16 +69,14 @@
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
If the ray did not intersect anything, then an empty dictionary is returned instead.
Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect (all layers by default), or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively.
</description>
</method>
<method name="intersect_shape">
<return type="Array" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters2D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters2D" />
<argument index="1" name="max_results" type="int" default="32" />
<description>
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters2D] object, against the space.
[b]Note:[/b] This method does not take into account the [code]motion[/code] property of the object. The intersected shapes are returned in an array containing dictionaries with the following fields:
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters2D] object, against the space. The intersected shapes are returned in an array containing dictionaries with the following fields:
[code]collider[/code]: The colliding object.
[code]collider_id[/code]: The colliding object's ID.
[code]rid[/code]: The intersecting object's [RID].

View file

@ -13,8 +13,7 @@
<methods>
<method name="cast_motion">
<return type="Array" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters3D" />
<argument index="1" name="motion" type="Vector3" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters3D" />
<description>
Checks how far a [Shape3D] can move without colliding. All the parameters for the query, including the shape, are supplied through a [PhysicsShapeQueryParameters3D] object.
Returns an array with the safe and unsafe proportions (between 0 and 1) of the motion. The safe proportion is the maximum fraction of the motion that can be made without a collision. The unsafe proportion is the minimum fraction of the distance that must be moved for a collision. If no collision is detected a result of [code][1.0, 1.0][/code] will be returned.
@ -23,16 +22,17 @@
</method>
<method name="collide_shape">
<return type="Array" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters3D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters3D" />
<argument index="1" name="max_results" type="int" default="32" />
<description>
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters3D] object, against the space. The resulting array contains a list of points where the shape intersects another. Like with [method intersect_shape], the number of returned results can be limited to save processing time.
Returned points are a list of pairs of contact points. For each pair the first one is in the shape passed in [PhysicsShapeQueryParameters3D] object, second one is in the collided shape from the physics space.
[b]Note:[/b] This method does not take into account the [code]motion[/code] property of the object.
</description>
</method>
<method name="get_rest_info">
<return type="Dictionary" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters3D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters3D" />
<description>
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters3D] object, against the space. If it collides with more than one shape, the nearest one is selected. The returned object is a dictionary containing the following fields:
[code]collider_id[/code]: The colliding object's ID.
@ -42,18 +42,27 @@
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
If the shape did not intersect anything, then an empty dictionary is returned instead.
[b]Note:[/b] This method does not take into account the [code]motion[/code] property of the object.
</description>
</method>
<method name="intersect_point">
<return type="Array" />
<argument index="0" name="parameters" type="PhysicsPointQueryParameters3D" />
<argument index="1" name="max_results" type="int" default="32" />
<description>
Checks whether a point is inside any solid shape. Position and other parameters are defined through [PhysicsPointQueryParameters3D]. The shapes the point is inside of are returned in an array containing dictionaries with the following fields:
[code]collider[/code]: The colliding object.
[code]collider_id[/code]: The colliding object's ID.
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
The number of intersections can be limited with the [code]max_results[/code] parameter, to reduce the processing time.
</description>
</method>
<method name="intersect_ray">
<return type="Dictionary" />
<argument index="0" name="from" type="Vector3" />
<argument index="1" name="to" type="Vector3" />
<argument index="2" name="exclude" type="Array" default="[]" />
<argument index="3" name="collision_mask" type="int" default="4294967295" />
<argument index="4" name="collide_with_bodies" type="bool" default="true" />
<argument index="5" name="collide_with_areas" type="bool" default="false" />
<argument index="0" name="parameters" type="PhysicsRayQueryParameters3D" />
<description>
Intersects a ray in a given space. The returned object is a dictionary with the following fields:
Intersects a ray in a given space. Ray position and other parameters are defined through [PhysicsRayQueryParameters3D]. The returned object is a dictionary with the following fields:
[code]collider[/code]: The colliding object.
[code]collider_id[/code]: The colliding object's ID.
[code]normal[/code]: The object's surface normal at the intersection point.
@ -61,12 +70,11 @@
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
If the ray did not intersect anything, then an empty dictionary is returned instead.
Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect (all layers by default), or booleans to determine if the ray should collide with [PhysicsBody3D]s or [Area3D]s, respectively.
</description>
</method>
<method name="intersect_shape">
<return type="Array" />
<argument index="0" name="shape" type="PhysicsShapeQueryParameters3D" />
<argument index="0" name="parameters" type="PhysicsShapeQueryParameters3D" />
<argument index="1" name="max_results" type="int" default="32" />
<description>
Checks the intersections of a shape, given through a [PhysicsShapeQueryParameters3D] object, against the space. The intersected shapes are returned in an array containing dictionaries with the following fields:
@ -75,6 +83,7 @@
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
The number of intersections can be limited with the [code]max_results[/code] parameter, to reduce the processing time.
[b]Note:[/b] This method does not take into account the [code]motion[/code] property of the object.
</description>
</method>
</methods>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicsPointQueryParameters2D" inherits="RefCounted" version="4.0">
<brief_description>
Parameters to be sent to a 2D point physics query.
</brief_description>
<description>
This class contains the position and other parameters to be used for [method PhysicsDirectSpaceState2D.intersect_point].
</description>
<tutorials>
</tutorials>
<members>
<member name="canvas_instance_id" type="int" setter="set_canvas_instance_id" getter="get_canvas_instance_id" default="0">
If different from [code]0[/code], restricts the query to a specific canvas layer specified by its instance id. See [method Object.get_instance_id].
</member>
<member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled" default="false">
If [code]true[/code], the query will take [Area2D]s into account.
</member>
<member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled" default="true">
If [code]true[/code], the query will take [PhysicsBody2D]s into account.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="4294967295">
The physics layers the query will detect (as a bitmask). By default, all collision layers are detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="exclude" type="Array" setter="set_exclude" getter="get_exclude" default="[]">
The list of objects or object [RID]s that will be excluded from collisions.
</member>
<member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2(0, 0)">
The position being queried for, in global coordinates.
</member>
</members>
</class>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicsPointQueryParameters3D" inherits="RefCounted" version="4.0">
<brief_description>
Parameters to be sent to a 3D point physics query.
</brief_description>
<description>
This class contains the position and other parameters to be used for [method PhysicsDirectSpaceState3D.intersect_point].
</description>
<tutorials>
</tutorials>
<members>
<member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled" default="false">
If [code]true[/code], the query will take [Area3D]s into account.
</member>
<member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled" default="true">
If [code]true[/code], the query will take [PhysicsBody3D]s into account.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="4294967295">
The physics layers the query will detect (as a bitmask). By default, all collision layers are detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="exclude" type="Array" setter="set_exclude" getter="get_exclude" default="[]">
The list of objects or object [RID]s that will be excluded from collisions.
</member>
<member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)">
The position being queried for, in global coordinates.
</member>
</members>
</class>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicsRayQueryParameters2D" inherits="RefCounted" version="4.0">
<brief_description>
Parameters to be sent to a 2D ray physics query.
</brief_description>
<description>
This class contains the ray position and other parameters to be used for [method PhysicsDirectSpaceState2D.intersect_ray].
</description>
<tutorials>
</tutorials>
<members>
<member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled" default="false">
If [code]true[/code], the query will take [Area2D]s into account.
</member>
<member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled" default="true">
If [code]true[/code], the query will take [PhysicsBody2D]s into account.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="4294967295">
The physics layers the query will detect (as a bitmask). By default, all collision layers are detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="exclude" type="Array" setter="set_exclude" getter="get_exclude" default="[]">
The list of objects or object [RID]s that will be excluded from collisions.
</member>
<member name="from" type="Vector2" setter="set_from" getter="get_from" default="Vector2(0, 0)">
The starting point of the ray being queried for, in global coordinates.
</member>
<member name="to" type="Vector2" setter="set_to" getter="get_to" default="Vector2(0, 0)">
The ending point of the ray being queried for, in global coordinates.
</member>
</members>
</class>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicsRayQueryParameters3D" inherits="RefCounted" version="4.0">
<brief_description>
Parameters to be sent to a 3D ray physics query.
</brief_description>
<description>
This class contains the ray position and other parameters to be used for [method PhysicsDirectSpaceState3D.intersect_ray].
</description>
<tutorials>
</tutorials>
<members>
<member name="collide_with_areas" type="bool" setter="set_collide_with_areas" getter="is_collide_with_areas_enabled" default="false">
If [code]true[/code], the query will take [Area3D]s into account.
</member>
<member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled" default="true">
If [code]true[/code], the query will take [PhysicsBody3D]s into account.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="4294967295">
The physics layers the query will detect (as a bitmask). By default, all collision layers are detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="exclude" type="Array" setter="set_exclude" getter="get_exclude" default="[]">
The list of objects or object [RID]s that will be excluded from collisions.
</member>
<member name="from" type="Vector3" setter="set_from" getter="get_from" default="Vector3(0, 0, 0)">
The starting point of the ray being queried for, in global coordinates.
</member>
<member name="to" type="Vector3" setter="set_to" getter="get_to" default="Vector3(0, 0, 0)">
The ending point of the ray being queried for, in global coordinates.
</member>
</members>
</class>

View file

@ -346,7 +346,7 @@
<return type="PhysicsDirectBodyState2D" />
<argument index="0" name="body" type="RID" />
<description>
Returns the [PhysicsDirectBodyState2D] of the body.
Returns the [PhysicsDirectBodyState2D] of the body. Returns [code]null[/code] if the body is destroyed or removed from the physics space.
</description>
</method>
<method name="body_get_max_contacts_reported" qualifiers="const">
@ -919,7 +919,7 @@
Constant to set/get a body's inertia.
</constant>
<constant name="BODY_PARAM_CENTER_OF_MASS" value="4" enum="BodyParameter">
Constant to set/get a body's center of mass.
Constant to set/get a body's center of mass position in the body's local coordinate system.
</constant>
<constant name="BODY_PARAM_GRAVITY_SCALE" value="5" enum="BodyParameter">
Constant to set/get a body's gravity multiplier.

View file

@ -320,7 +320,7 @@
<return type="PhysicsDirectBodyState3D" />
<argument index="0" name="body" type="RID" />
<description>
Returns the [PhysicsDirectBodyState3D] of the body.
Returns the [PhysicsDirectBodyState3D] of the body. Returns [code]null[/code] if the body is destroyed or removed from the physics space.
</description>
</method>
<method name="body_get_max_contacts_reported" qualifiers="const">
@ -1287,7 +1287,7 @@
Constant to set/get a body's inertia.
</constant>
<constant name="BODY_PARAM_CENTER_OF_MASS" value="4" enum="BodyParameter">
Constant to set/get a body's center of mass.
Constant to set/get a body's center of mass position in the body's local coordinate system.
</constant>
<constant name="BODY_PARAM_GRAVITY_SCALE" value="5" enum="BodyParameter">
Constant to set/get a body's gravity multiplier.

View file

@ -4,7 +4,7 @@
Parameters to be sent to a 2D shape physics query.
</brief_description>
<description>
This class contains the shape and other parameters for 2D intersection/collision queries.
This class contains the shape and other parameters for [PhysicsDirectSpaceState2D] intersection/collision queries.
</description>
<tutorials>
</tutorials>

View file

@ -4,7 +4,7 @@
Parameters to be sent to a 3D shape physics query.
</brief_description>
<description>
This class contains the shape and other parameters for 3D intersection/collision queries.
This class contains the shape and other parameters for [PhysicsDirectSpaceState3D] intersection/collision queries.
</description>
<tutorials>
</tutorials>
@ -24,6 +24,9 @@
<member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.0">
The collision margin for the shape.
</member>
<member name="motion" type="Vector3" setter="set_motion" getter="get_motion" default="Vector3(0, 0, 0)">
The motion of the shape being queried for.
</member>
<member name="shape" type="Resource" setter="set_shape" getter="get_shape">
The [Shape3D] that will be used for collision/intersection queries. This stores the actual reference which avoids the shape to be released while being used for queries, so always prefer using this over [member shape_rid].
</member>

View file

@ -1315,9 +1315,9 @@
</member>
<member name="mono/profiler/enabled" type="bool" setter="" getter="" default="false">
</member>
<member name="mono/project/auto_update_project" type="bool" setter="" getter="" default="true">
</member>
<member name="mono/unhandled_exception_policy" type="int" setter="" getter="" default="0">
<member name="mono/runtime/unhandled_exception_policy" type="int" setter="" getter="" default="0">
The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown.
[b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code.
</member>
<member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="10">
Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size].

View file

@ -2575,12 +2575,9 @@
</method>
<method name="request_frame_drawn_callback">
<return type="void" />
<argument index="0" name="where" type="Object" />
<argument index="1" name="method" type="StringName" />
<argument index="2" name="userdata" type="Variant" />
<argument index="0" name="callable" type="Callable" />
<description>
Schedules a callback to the corresponding named [code]method[/code] on [code]where[/code] after a frame has been drawn.
The callback method must use only 1 argument which will be called with [code]userdata[/code].
Schedules a callback to the given callable after a frame has been drawn.
</description>
</method>
<method name="scenario_create">

View file

@ -586,15 +586,15 @@
<theme_item name="selection_color" data_type="color" type="Color" default="Color(0.1, 0.1, 1, 0.8)">
The color of the selection box.
</theme_item>
<theme_item name="shadow_as_outline" data_type="constant" type="int" default="0">
Boolean value. If 1 ([code]true[/code]), the shadow will be displayed around the whole text as an outline.
</theme_item>
<theme_item name="shadow_offset_x" data_type="constant" type="int" default="1">
The horizontal offset of the font's shadow.
</theme_item>
<theme_item name="shadow_offset_y" data_type="constant" type="int" default="1">
The vertical offset of the font's shadow.
</theme_item>
<theme_item name="shadow_outline_size" data_type="constant" type="int" default="1">
The size of the shadow outline.
</theme_item>
<theme_item name="table_border" data_type="color" type="Color" default="Color(0, 0, 0, 0)">
The default cell border color.
</theme_item>

View file

@ -168,5 +168,9 @@
If passed to [method PackedScene.instantiate], provides local scene resources to the local scene. Only the main scene should receive the main edit state.
[b]Note:[/b] Only available in editor builds.
</constant>
<constant name="GEN_EDIT_STATE_MAIN_INHERITED" value="3" enum="GenEditState">
If passed to [method PackedScene.instantiate], it's similar to [constant GEN_EDIT_STATE_MAIN], but for the case where the scene is being instantiated to be the base of another one.
[b]Note:[/b] Only available in editor builds.
</constant>
</constants>
</class>

View file

@ -278,6 +278,9 @@
<member name="align" type="int" setter="set_align" getter="get_align" enum="HAlign" default="0">
Paragraph horizontal alignment.
</member>
<member name="custom_punctuation" type="String" setter="set_custom_punctuation" getter="get_custom_punctuation" default="&quot;&quot;">
Custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</member>
<member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0">
Text writing direction.
</member>

View file

@ -254,6 +254,13 @@
Returns source font size used to generate MSDF textures.
</description>
</method>
<method name="font_get_name" qualifiers="const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font family name.
</description>
</method>
<method name="font_get_oversampling" qualifiers="const">
<return type="float" />
<argument index="0" name="font_rid" type="RID" />
@ -300,6 +307,20 @@
Returns extra spacing added between glyphs in pixels.
</description>
</method>
<method name="font_get_style" qualifiers="const">
<return type="int" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font style flags, see [enum FontStyle].
</description>
</method>
<method name="font_get_style_name" qualifiers="const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font style name.
</description>
</method>
<method name="font_get_supported_chars" qualifiers="const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
@ -634,6 +655,14 @@
[b]Note:[/b] MSDF font rendering does not render glyphs with overlapping shapes correctly. Overlapping shapes are not valid per the OpenType standard, but are still commonly found in many font files, especially those converted by Google Fonts. To avoid issues with overlapping glyphs, consider downloading the font file directly from the type foundry instead of relying on Google Fonts.
</description>
</method>
<method name="font_set_name">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="name" type="String" />
<description>
Sets the font family name.
</description>
</method>
<method name="font_set_oversampling">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@ -670,6 +699,22 @@
Sets extra spacing added between glyphs in pixels.
</description>
</method>
<method name="font_set_style">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="style" type="int" />
<description>
Sets the font style flags, see [enum FontStyle].
</description>
</method>
<method name="font_set_style_name">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="name" type="String" />
<description>
Set the font style name.
</description>
</method>
<method name="font_set_texture_image">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@ -917,6 +962,13 @@
Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
</description>
</method>
<method name="shaped_text_get_custom_punctuation" qualifiers="const">
<return type="String" />
<argument index="0" name="shaped" type="RID" />
<description>
Returns custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</description>
</method>
<method name="shaped_text_get_descent" qualifiers="const">
<return type="float" />
<argument index="0" name="shaped" type="RID" />
@ -1167,6 +1219,14 @@
Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
<method name="shaped_text_set_custom_punctuation">
<return type="void" />
<argument index="0" name="shaped" type="RID" />
<argument index="1" name="punct" type="String" />
<description>
Sets custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</description>
</method>
<method name="shaped_text_set_direction">
<return type="void" />
<argument index="0" name="shaped" type="RID" />
@ -1402,5 +1462,14 @@
<constant name="SPACING_BOTTOM" value="3" enum="SpacingType">
Spacing at the bottom of the line.
</constant>
<constant name="FONT_BOLD" value="1" enum="FontStyle">
Font is bold.
</constant>
<constant name="FONT_ITALIC" value="2" enum="FontStyle">
Font is italic or oblique.
</constant>
<constant name="FONT_FIXED_WIDTH" value="4" enum="FontStyle">
Font have fixed-width characters.
</constant>
</constants>
</class>

View file

@ -254,6 +254,13 @@
Returns source font size used to generate MSDF textures.
</description>
</method>
<method name="_font_get_name" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font family name.
</description>
</method>
<method name="_font_get_oversampling" qualifiers="virtual const">
<return type="float" />
<argument index="0" name="font_rid" type="RID" />
@ -300,6 +307,20 @@
Returns extra spacing added between glyphs in pixels.
</description>
</method>
<method name="_font_get_style" qualifiers="virtual const">
<return type="int" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="_font_get_style_name" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font style name.
</description>
</method>
<method name="_font_get_supported_chars" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
@ -641,6 +662,14 @@
If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
</description>
</method>
<method name="_font_set_name" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="name" type="String" />
<description>
Sets the font family name.
</description>
</method>
<method name="_font_set_oversampling" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@ -677,6 +706,22 @@
Sets extra spacing added between glyphs in pixels.
</description>
</method>
<method name="_font_set_style" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="style" type="int" />
<description>
Sets the font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="_font_set_style_name" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="name_style" type="String" />
<description>
Sets the font style name.
</description>
</method>
<method name="_font_set_texture_image" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@ -924,6 +969,13 @@
Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
</description>
</method>
<method name="_shaped_text_get_custom_punctuation" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="shaped" type="RID" />
<description>
Returns custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</description>
</method>
<method name="_shaped_text_get_descent" qualifiers="virtual const">
<return type="float" />
<argument index="0" name="shaped" type="RID" />
@ -1176,6 +1228,14 @@
Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
<method name="_shaped_text_set_custom_punctuation" qualifiers="virtual">
<return type="void" />
<argument index="0" name="shaped" type="RID" />
<argument index="1" name="punct" type="String" />
<description>
Sets custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</description>
</method>
<method name="_shaped_text_set_direction" qualifiers="virtual">
<return type="void" />
<argument index="0" name="shaped" type="RID" />

View file

@ -27,8 +27,8 @@
<return type="String" />
<argument index="0" name="input_vars" type="PackedStringArray" />
<argument index="1" name="output_vars" type="String[]" />
<argument index="2" name="mode" type="int" />
<argument index="3" name="type" type="int" />
<argument index="2" name="mode" type="int" enum="Shader.Mode" />
<argument index="3" name="type" type="int" enum="VisualShader.Type" />
<description>
Override this method to define the actual shader code of the associated custom node. The shader code should be returned as a string, which can have multiple lines (the [code]"""[/code] multiline string construct can be used for convenience).
The [code]input_vars[/code] and [code]output_vars[/code] arrays contain the string names of the various input and output variables, as defined by [code]_get_input_*[/code] and [code]_get_output_*[/code] virtual methods in this class.
@ -46,7 +46,7 @@
</method>
<method name="_get_global_code" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="mode" type="int" />
<argument index="0" name="mode" type="int" enum="Shader.Mode" />
<description>
Override this method to add shader code on top of the global shader, to define your own standard library of reusable methods, varyings, constants, uniforms, etc. The shader code should be returned as a string, which can have multiple lines (the [code]"""[/code] multiline string construct can be used for convenience).
Be careful with this functionality as it can cause name conflicts with other custom nodes, so be sure to give the defined entities unique names.

View file

@ -1,9 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeParticleEmitter" inherits="VisualShaderNode" version="4.0">
<brief_description>
A base class for particle emitters.
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<members>
<member name="mode_2d" type="bool" setter="set_mode_2d" getter="is_mode_2d" default="false">
If [code]true[/code], the result of this emitter is projected to 2D space. By default it is [code]false[/code] and meant for use in 3D space.
</member>
</members>
</class>

View file

@ -34,6 +34,7 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/version.h"
#ifdef ALSAMIDI_ENABLED
#include "drivers/alsa/asound-so_wrap.h"
@ -191,7 +192,7 @@ Error AudioDriverPulseAudio::init_device() {
Error err = detect_channels();
if (err != OK) {
// This most likely means there are no sinks.
ERR_PRINT("PulseAudio: init device failed to detect number of channels");
ERR_PRINT("PulseAudio: init device failed to detect number of output channels");
return err;
}
@ -211,7 +212,7 @@ Error AudioDriverPulseAudio::init_device() {
break;
default:
WARN_PRINT("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels));
WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels));
pa_channel_map_init_stereo(&pa_map);
channels = 2;
break;
@ -221,8 +222,8 @@ Error AudioDriverPulseAudio::init_device() {
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
pa_buffer_size = buffer_frames * pa_map.channels;
print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " channels");
print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
@ -293,7 +294,17 @@ Error AudioDriverPulseAudio::init() {
pa_ml = pa_mainloop_new();
ERR_FAIL_COND_V(pa_ml == nullptr, ERR_CANT_OPEN);
pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot");
String context_name;
if (Engine::get_singleton()->is_editor_hint()) {
context_name = VERSION_NAME " Editor";
} else {
context_name = GLOBAL_GET("application/config/name");
if (context_name.is_empty()) {
context_name = VERSION_NAME " Project";
}
}
pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), context_name.utf8().ptr());
ERR_FAIL_COND_V(pa_ctx == nullptr, ERR_CANT_OPEN);
pa_ready = 0;
@ -689,6 +700,8 @@ Error AudioDriverPulseAudio::capture_init_device() {
break;
}
print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;

View file

@ -121,6 +121,12 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
static bool default_render_device_changed = false;
static bool default_capture_device_changed = false;
// Silence warning due to a COM API weirdness (GH-35194).
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#endif
class CMMNotificationClient : public IMMNotificationClient {
LONG _cRef = 1;
IMMDeviceEnumerator *_pEnumerator = nullptr;
@ -162,7 +168,7 @@ public:
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) {
return S_OK;
};
}
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
return S_OK;
@ -189,6 +195,10 @@ public:
}
};
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
static CMMNotificationClient notif_client;
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) {
@ -373,7 +383,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr);
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + ".");
UINT32 max_frames;
HRESULT hr = p_device->audio_client->GetBufferSize(&max_frames);
hr = p_device->audio_client->GetBufferSize(&max_frames);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
// Due to WASAPI Shared Mode we have no control of the buffer size

View file

@ -1377,8 +1377,20 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
void AnimationTimelineEdit::_anim_loop_pressed() {
undo_redo->create_action(TTR("Change Animation Loop"));
undo_redo->add_do_method(animation.ptr(), "set_loop", loop->is_pressed());
undo_redo->add_undo_method(animation.ptr(), "set_loop", animation->has_loop());
switch (animation->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_LINEAR);
} break;
case Animation::LoopMode::LOOP_LINEAR: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_PINGPONG);
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_NONE);
} break;
default:
break;
}
undo_redo->add_undo_method(animation.ptr(), "set_loop_mode", animation->get_loop_mode());
undo_redo->commit_action();
}
@ -1664,7 +1676,24 @@ void AnimationTimelineEdit::update_values() {
length->set_tooltip(TTR("Animation length (seconds)"));
time_icon->set_tooltip(TTR("Animation length (seconds)"));
}
loop->set_pressed(animation->has_loop());
switch (animation->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
loop->set_icon(get_theme_icon("Loop", "EditorIcons"));
loop->set_pressed(false);
} break;
case Animation::LoopMode::LOOP_LINEAR: {
loop->set_icon(get_theme_icon("Loop", "EditorIcons"));
loop->set_pressed(true);
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
loop->set_icon(get_theme_icon("PingPongLoop", "EditorIcons"));
loop->set_pressed(true);
} break;
default:
break;
}
editing = false;
}
@ -2110,25 +2139,25 @@ void AnimationTrackEdit::_notification(int p_what) {
Ref<Texture2D> icon = wrap_icon[loop_wrap ? 1 : 0];
loop_mode_rect.position.x = ofs;
loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
loop_mode_rect.size = icon->get_size();
loop_wrap_rect.position.x = ofs;
loop_wrap_rect.position.y = int(get_size().height - icon->get_height()) / 2;
loop_wrap_rect.size = icon->get_size();
if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(icon, loop_mode_rect.position);
draw_texture(icon, loop_wrap_rect.position);
}
loop_mode_rect.position.y = 0;
loop_mode_rect.size.y = get_size().height;
loop_wrap_rect.position.y = 0;
loop_wrap_rect.size.y = get_size().height;
ofs += icon->get_width() + hsep;
loop_mode_rect.size.x += hsep;
loop_wrap_rect.size.x += hsep;
if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
loop_mode_rect.size.x += down_icon->get_width();
loop_wrap_rect.size.x += down_icon->get_width();
} else {
loop_mode_rect = Rect2();
loop_wrap_rect = Rect2();
}
ofs += down_icon->get_width();
@ -2478,7 +2507,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
return TTR("Interpolation Mode");
}
if (loop_mode_rect.has_point(p_pos)) {
if (loop_wrap_rect.has_point(p_pos)) {
return TTR("Loop Wrap Mode (Interpolate end with beginning on loop)");
}
@ -2681,7 +2710,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
accept_event();
}
if (loop_mode_rect.has_point(pos)) {
if (loop_wrap_rect.has_point(pos)) {
if (!menu) {
menu = memnew(PopupMenu);
add_child(menu);
@ -2692,7 +2721,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->add_icon_item(get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), TTR("Wrap Loop Interp"), MENU_LOOP_WRAP);
menu->set_as_minsize();
Vector2 popup_pos = get_screen_position() + loop_mode_rect.position + Vector2(0, loop_mode_rect.size.height);
Vector2 popup_pos = get_screen_position() + loop_wrap_rect.position + Vector2(0, loop_wrap_rect.size.height);
menu->set_position(popup_pos);
menu->popup();
accept_event();

View file

@ -159,7 +159,7 @@ class AnimationTrackEdit : public Control {
Rect2 update_mode_rect;
Rect2 interp_mode_rect;
Rect2 loop_mode_rect;
Rect2 loop_wrap_rect;
Rect2 remove_rect;
Rect2 bezier_edit_rect;
@ -466,6 +466,7 @@ class AnimationTrackEditor : public VBoxContainer {
Animation::TrackType track_type = Animation::TrackType::TYPE_ANIMATION;
Animation::InterpolationType interp_type = Animation::InterpolationType::INTERPOLATION_CUBIC;
Animation::UpdateMode update_mode = Animation::UpdateMode::UPDATE_CAPTURE;
Animation::LoopMode loop_mode = Animation::LoopMode::LOOP_LINEAR;
bool loop_wrap = false;
bool enabled = false;

View file

@ -39,6 +39,7 @@
#include "editor_scale.h"
#include "editor_settings.h"
#include "multi_node_edit.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
Size2 EditorProperty::get_minimum_size() const {
@ -305,6 +306,20 @@ void EditorProperty::_notification(int p_what) {
revert_rect = Rect2();
}
if (!pin_hidden && pinned) {
Ref<Texture2D> pinned_icon = get_theme_icon(SNAME("Pin"), SNAME("EditorIcons"));
int margin_w = get_theme_constant(SNAME("hseparator"), SNAME("Tree")) * 2;
int total_icon_w = margin_w + pinned_icon->get_width();
int text_w = font->get_string_size(label, font_size, rtl ? HALIGN_RIGHT : HALIGN_LEFT, text_limit - total_icon_w).x;
int y = (size.height - pinned_icon->get_height()) / 2;
if (rtl) {
draw_texture(pinned_icon, Vector2(size.width - ofs - text_w - total_icon_w, y), color);
} else {
draw_texture(pinned_icon, Vector2(ofs + text_w + margin_w, y), color);
}
text_limit -= total_icon_w;
}
int v_ofs = (size.height - font->get_height(font_size)) / 2;
if (rtl) {
draw_string(font, Point2(size.width - ofs - text_limit, v_ofs + font->get_ascent(font_size)), label, HALIGN_RIGHT, text_limit, font_size, color);
@ -398,177 +413,12 @@ bool EditorProperty::is_read_only() const {
return read_only;
}
bool EditorPropertyRevert::may_node_be_in_instance(Node *p_node) {
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
bool might_be = false;
Node *node = p_node;
while (node) {
if (node == edited_scene) {
if (node->get_scene_inherited_state().is_valid()) {
might_be = true;
break;
}
might_be = false;
break;
}
if (node->get_scene_instance_state().is_valid()) {
might_be = true;
break;
}
node = node->get_owner();
}
return might_be; // or might not be
}
bool EditorPropertyRevert::get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default) {
Node *node = p_node;
Node *orig = node;
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
bool found = false;
while (node) {
Ref<SceneState> ss;
if (node == edited_scene) {
ss = node->get_scene_inherited_state();
} else {
ss = node->get_scene_instance_state();
}
if (ss.is_valid()) {
NodePath np = node->get_path_to(orig);
int node_idx = ss->find_node_by_path(np);
if (node_idx >= 0) {
bool lfound = false;
Variant lvar;
lvar = ss->get_property_value(node_idx, p_prop, lfound);
if (lfound) {
found = true;
value = lvar;
}
}
}
if (node == edited_scene) {
//just in case
break;
}
node = node->get_owner();
}
if (p_check_class_default && !found && p_node) {
//if not found, try default class value
Variant attempt = ClassDB::class_get_default_property_value(p_node->get_class_name(), p_prop);
if (attempt.get_type() != Variant::NIL) {
found = true;
value = attempt;
}
}
return found;
}
bool EditorPropertyRevert::is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig) {
// this is a pretty difficult function, because a property may not be saved but may have
// the flag to not save if one or if zero
//make sure there is an actual state
{
Node *node = p_node;
if (!node) {
return false;
}
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
bool found_state = false;
while (node) {
Ref<SceneState> ss;
if (node == edited_scene) {
ss = node->get_scene_inherited_state();
} else {
ss = node->get_scene_instance_state();
}
if (ss.is_valid()) {
found_state = true;
break;
}
if (node == edited_scene) {
//just in case
break;
}
node = node->get_owner();
}
if (!found_state) {
return false; //pointless to check if we are not comparing against anything.
}
}
return is_property_value_different(p_current, p_orig);
}
bool EditorPropertyRevert::is_property_value_different(const Variant &p_a, const Variant &p_b) {
if (p_a.get_type() == Variant::FLOAT && p_b.get_type() == Variant::FLOAT) {
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
return !Math::is_equal_approx((float)p_a, (float)p_b);
} else {
return p_a != p_b;
}
}
Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const StringName &p_property) {
// If the object implements property_can_revert, rely on that completely
// (i.e. don't then try to revert to default value - the property_get_revert implementation
// can do that if so desired)
if (p_object->has_method("property_can_revert") && p_object->call("property_can_revert", p_property)) {
return p_object->call("property_get_revert", p_property);
}
Ref<Script> scr = p_object->get_script();
Node *node = Object::cast_to<Node>(p_object);
if (node && EditorPropertyRevert::may_node_be_in_instance(node)) {
//if this node is an instance or inherits, but it has a script attached which is unrelated
//to the one set for the parent and also has a default value for the property, consider that
//has precedence over the value from the parent, because that is an explicit source of defaults
//closer in the tree to the current node
bool ignore_parent = false;
if (scr.is_valid()) {
Variant sorig;
if (EditorPropertyRevert::get_instantiated_node_original_property(node, "script", sorig) && !scr->inherits_script(sorig)) {
Variant dummy;
if (scr->get_property_default_value(p_property, dummy)) {
ignore_parent = true;
}
}
}
if (!ignore_parent) {
//check for difference including instantiation
Variant vorig;
if (EditorPropertyRevert::get_instantiated_node_original_property(node, p_property, vorig, false)) {
return vorig;
}
}
}
if (scr.is_valid()) {
Variant orig_value;
if (scr->get_property_default_value(p_property, orig_value)) {
return orig_value;
}
}
//report default class value instead
return ClassDB::class_get_default_property_value(p_object->get_class_name(), p_property);
return PropertyUtils::get_property_default_value(p_object, p_property);
}
bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringName &p_property) {
@ -577,18 +427,25 @@ bool EditorPropertyRevert::can_property_revert(Object *p_object, const StringNam
return false;
}
Variant current_value = p_object->get(p_property);
return EditorPropertyRevert::is_property_value_different(current_value, revert_value);
return PropertyUtils::is_property_value_different(current_value, revert_value);
}
void EditorProperty::update_reload_status() {
void EditorProperty::update_revert_and_pin_status() {
if (property == StringName()) {
return; //no property, so nothing to do
}
bool has_reload = EditorPropertyRevert::can_property_revert(object, property);
bool new_pinned = false;
if (can_pin) {
Node *node = Object::cast_to<Node>(object);
CRASH_COND(!node);
new_pinned = node->is_property_pinned(property);
}
bool new_can_revert = EditorPropertyRevert::can_property_revert(object, property) && !is_read_only();
if (has_reload != can_revert) {
can_revert = has_reload;
if (new_can_revert != can_revert || new_pinned != pinned) {
can_revert = new_can_revert;
pinned = new_pinned;
update();
}
}
@ -791,7 +648,7 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
emit_signal(SNAME("property_checked"), property, checked);
}
} else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
_ensure_popup();
_update_popup();
menu->set_position(get_screen_position() + get_local_mouse_position());
menu->set_size(Vector2(1, 1));
menu->popup();
@ -914,6 +771,56 @@ float EditorProperty::get_name_split_ratio() const {
void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) {
object = p_object;
property = p_property;
_update_pin_flags();
}
static bool _is_value_potential_override(Node *p_node, const String &p_property) {
// Consider a value is potentially overriding another if either of the following is true:
// a) The node is foreign (inheriting or an instance), so the original value may come from another scene.
// b) The node belongs to the scene, but the original value comes from somewhere but the builtin class (i.e., a script).
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, edited_scene);
if (states_stack.size()) {
return true;
} else {
bool is_class_default = false;
PropertyUtils::get_property_default_value(p_node, p_property, &states_stack, false, nullptr, &is_class_default);
return !is_class_default;
}
}
void EditorProperty::_update_pin_flags() {
can_pin = false;
pin_hidden = true;
if (read_only) {
return;
}
if (Node *node = Object::cast_to<Node>(object)) {
// Avoid errors down the road by ignoring nodes which are not part of a scene
if (!node->get_owner()) {
bool is_scene_root = false;
for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_edited_scene_count(); ++i) {
if (EditorNode::get_singleton()->get_editor_data().get_edited_scene_root(i) == node) {
is_scene_root = true;
break;
}
}
if (!is_scene_root) {
return;
}
}
if (!_is_value_potential_override(node, property)) {
return;
}
pin_hidden = false;
{
Set<StringName> storable_properties;
node->get_storable_properties(storable_properties);
if (storable_properties.has(node->get_property_store_alias(property))) {
can_pin = true;
}
}
}
}
Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
@ -955,6 +862,10 @@ void EditorProperty::menu_option(int p_option) {
case MENU_COPY_PROPERTY_PATH: {
DisplayServer::get_singleton()->clipboard_set(property);
} break;
case MENU_PIN_VALUE: {
emit_signal(SNAME("property_pinned"), property, !pinned);
update();
} break;
}
}
@ -1003,12 +914,14 @@ void EditorProperty::_bind_methods() {
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING_NAME, "property")));
ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING_NAME, "property")));
ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::STRING, "bool")));
ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked")));
ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned")));
ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx")));
GDVIRTUAL_BIND(_update_property)
ClassDB::bind_method(D_METHOD("_update_revert_and_pin_status"), &EditorProperty::update_revert_and_pin_status);
}
EditorProperty::EditorProperty() {
@ -1027,6 +940,9 @@ EditorProperty::EditorProperty() {
revert_hover = false;
check_hover = false;
can_revert = false;
can_pin = false;
pin_hidden = false;
pinned = false;
use_folding = false;
property_usage = 0;
selected = false;
@ -1038,17 +954,29 @@ EditorProperty::EditorProperty() {
set_process_unhandled_key_input(true);
}
void EditorProperty::_ensure_popup() {
void EditorProperty::_update_popup() {
if (menu) {
return;
menu->clear();
} else {
menu = memnew(PopupMenu);
add_child(menu);
menu->connect("id_pressed", callable_mp(this, &EditorProperty::menu_option));
}
menu = memnew(PopupMenu);
menu->add_shortcut(ED_GET_SHORTCUT("property_editor/copy_property"), MENU_COPY_PROPERTY);
menu->add_shortcut(ED_GET_SHORTCUT("property_editor/paste_property"), MENU_PASTE_PROPERTY);
menu->add_shortcut(ED_GET_SHORTCUT("property_editor/copy_property_path"), MENU_COPY_PROPERTY_PATH);
menu->connect("id_pressed", callable_mp(this, &EditorProperty::menu_option));
menu->set_item_disabled(MENU_PASTE_PROPERTY, is_read_only());
add_child(menu);
if (!pin_hidden) {
menu->add_separator();
if (can_pin) {
menu->add_check_item(TTR("Pin value"), MENU_PIN_VALUE);
menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), pinned);
menu->set_item_tooltip(menu->get_item_index(MENU_PIN_VALUE), TTR("Pinning a value forces it to be saved even if it's equal to the default."));
} else {
menu->add_check_item(vformat(TTR("Pin value [Disabled because '%s' is editor-only]"), property), MENU_PIN_VALUE);
menu->set_item_disabled(menu->get_item_index(MENU_PIN_VALUE), true);
}
}
}
////////////////////////////////////////////////
@ -2296,6 +2224,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit
ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), CONNECT_DEFERRED);
ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value));
ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked));
ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned));
ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected));
ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed));
ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), varray(), CONNECT_DEFERRED);
@ -2324,7 +2253,8 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, Ref<Edit
ep->set_read_only(read_only);
ep->update_property();
ep->update_reload_status();
ep->_update_pin_flags();
ep->update_revert_and_pin_status();
ep->set_deletable(deletable_properties);
ep->update_cache();
}
@ -2877,6 +2807,7 @@ void EditorInspector::update_tree() {
ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), varray(), CONNECT_DEFERRED);
ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value));
ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked));
ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned));
ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected));
ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed));
ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), varray(), CONNECT_DEFERRED);
@ -2887,7 +2818,8 @@ void EditorInspector::update_tree() {
ep->set_tooltip(property_prefix + p.name);
}
ep->update_property();
ep->update_reload_status();
ep->_update_pin_flags();
ep->update_revert_and_pin_status();
ep->update_cache();
if (current_selected && ep->property == current_selected) {
@ -2917,7 +2849,7 @@ void EditorInspector::update_property(const String &p_prop) {
for (EditorProperty *E : editor_property_map[p_prop]) {
E->update_property();
E->update_reload_status();
E->update_revert_and_pin_status();
E->update_cache();
}
}
@ -3196,7 +3128,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
if (editor_property_map.has(p_name)) {
for (EditorProperty *E : editor_property_map[p_name]) {
E->update_reload_status();
E->update_revert_and_pin_status();
}
}
}
@ -3295,7 +3227,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
if (editor_property_map.has(p_path)) {
for (EditorProperty *E : editor_property_map[p_path]) {
E->update_property();
E->update_reload_status();
E->update_revert_and_pin_status();
E->update_cache();
}
}
@ -3305,6 +3237,35 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
}
}
void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) {
if (!object) {
return;
}
Node *node = Object::cast_to<Node>(object);
ERR_FAIL_COND(!node);
if (undo_redo) {
undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned);
undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned);
if (editor_property_map.has(p_path)) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
undo_redo->add_do_method(E->get(), "_update_revert_and_pin_status");
undo_redo->add_undo_method(E->get(), "_update_revert_and_pin_status");
}
}
undo_redo->commit_action();
} else {
node->set_property_pinned(p_path, p_pinned);
if (editor_property_map.has(p_path)) {
for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
E->get()->update_revert_and_pin_status();
}
}
}
}
void EditorInspector::_property_selected(const String &p_path, int p_focusable) {
property_selected = p_path;
property_focusable = p_focusable;
@ -3375,7 +3336,7 @@ void EditorInspector::_notification(int p_what) {
for (EditorProperty *E : F.value) {
if (!E->is_cache_valid()) {
E->update_property();
E->update_reload_status();
E->update_revert_and_pin_status();
E->update_cache();
}
}
@ -3397,7 +3358,7 @@ void EditorInspector::_notification(int p_what) {
if (editor_property_map.has(prop)) {
for (EditorProperty *E : editor_property_map[prop]) {
E->update_property();
E->update_reload_status();
E->update_revert_and_pin_status();
E->update_cache();
}
}

View file

@ -43,7 +43,6 @@ class UndoRedo;
class EditorPropertyRevert {
public:
static bool may_node_be_in_instance(Node *p_node);
static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true);
static bool is_node_property_different(Node *p_node, const Variant &p_current, const Variant &p_orig);
static bool is_property_value_different(const Variant &p_a, const Variant &p_b);
@ -60,6 +59,7 @@ public:
MENU_COPY_PROPERTY,
MENU_PASTE_PROPERTY,
MENU_COPY_PROPERTY_PATH,
MENU_PIN_VALUE,
};
private:
@ -91,11 +91,14 @@ private:
bool delete_hover = false;
bool can_revert;
bool can_pin;
bool pin_hidden;
bool pinned;
bool use_folding;
bool draw_top_bg;
void _ensure_popup();
void _update_popup();
void _focusable_focused(int p_index);
bool selectable;
@ -114,6 +117,8 @@ private:
Map<StringName, Variant> cache;
GDVIRTUAL0(_update_property)
void _update_pin_flags();
protected:
void _notification(int p_what);
static void _bind_methods();
@ -138,7 +143,7 @@ public:
StringName get_edited_property();
virtual void update_property();
void update_reload_status();
void update_revert_and_pin_status();
virtual bool use_keying_next() const;
@ -459,8 +464,8 @@ class EditorInspector : public ScrollContainer {
void _property_keyed(const String &p_path, bool p_advance);
void _property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance);
void _property_deleted(const String &p_path);
void _property_checked(const String &p_path, bool p_checked);
void _property_pinned(const String &p_path, bool p_pinned);
void _resource_selected(const String &p_path, RES p_resource);
void _property_selected(const String &p_path, int p_focusable);

View file

@ -45,9 +45,9 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
String err_str;
if (p_errorexp && p_errorexp[0]) {
err_str = p_errorexp;
err_str = String::utf8(p_errorexp);
} else {
err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error);
err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error);
}
if (p_editor_notify) {

View file

@ -174,6 +174,7 @@
#include "editor/plugins/sprite_frames_editor_plugin.h"
#include "editor/plugins/style_box_editor_plugin.h"
#include "editor/plugins/sub_viewport_preview_editor_plugin.h"
#include "editor/plugins/text_control_editor_plugin.h"
#include "editor/plugins/text_editor.h"
#include "editor/plugins/texture_3d_editor_plugin.h"
#include "editor/plugins/texture_editor_plugin.h"
@ -1716,8 +1717,10 @@ void EditorNode::_save_scene(String p_file, int idx) {
err = ResourceSaver::save(p_file, sdata, flg);
_save_external_resources();
// This needs to be emitted before saving external resources.
emit_signal(SNAME("scene_saved"), p_file);
_save_external_resources();
editor_data.save_editor_external_data();
for (Ref<AnimatedValuesBackup> &E : anim_backups) {
@ -1791,7 +1794,7 @@ void EditorNode::_save_all_scenes() {
} else {
_save_scene_with_preview(scene->get_scene_file_path());
}
} else {
} else if (scene->get_scene_file_path() != "") {
all_saved = false;
}
}
@ -3623,7 +3626,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
sdata->set_path(lpath, true); // take over path
}
Node *new_scene = sdata->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
Node *new_scene = sdata->instantiate(p_set_inherited ? PackedScene::GEN_EDIT_STATE_MAIN_INHERITED : PackedScene::GEN_EDIT_STATE_MAIN);
if (!new_scene) {
sdata.unref();
@ -5711,6 +5714,7 @@ void EditorNode::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_help_search"));
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("project_settings_changed"));
}
@ -7010,6 +7014,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(GPUParticlesCollisionSDFEditorPlugin(this)));
add_editor_plugin(memnew(InputEventEditorPlugin(this)));
add_editor_plugin(memnew(SubViewportPreviewEditorPlugin(this)));
add_editor_plugin(memnew(TextControlEditorPlugin(this)));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
add_editor_plugin(EditorPlugins::create(i, this));

View file

@ -84,7 +84,7 @@ void EditorResourcePicker::_update_resource_preview(const String &p_path, const
if (p_preview.is_valid()) {
preview_rect->set_offset(SIDE_LEFT, assign_button->get_icon()->get_width() + assign_button->get_theme_stylebox(SNAME("normal"))->get_default_margin(SIDE_LEFT) + get_theme_constant(SNAME("hseparation"), SNAME("Button")));
if (type == "GradientTexture") {
if (type == "GradientTexture1D") {
preview_rect->set_stretch_mode(TextureRect::STRETCH_SCALE);
assign_button->set_custom_minimum_size(Size2(1, 1));
} else {

View file

@ -1207,7 +1207,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_shadow_color", "RichTextLabel", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * EDSCALE);
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * EDSCALE);
theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * EDSCALE);
theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * EDSCALE);
theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox());
theme->set_stylebox("normal", "RichTextLabel", style_tree_bg);
@ -1223,7 +1223,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_shadow_color", "Label", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "Label", 1 * EDSCALE);
theme->set_constant("shadow_offset_y", "Label", 1 * EDSCALE);
theme->set_constant("shadow_as_outline", "Label", 0 * EDSCALE);
theme->set_constant("shadow_outline_size", "Label", 1 * EDSCALE);
theme->set_constant("line_spacing", "Label", 3 * EDSCALE);
// LinkButton

View file

@ -158,11 +158,11 @@ void EditorToaster::_error_handler(void *p_self, const char *p_func, const char
if (p_editor_notify || (show_all_setting == 0 && in_dev) || show_all_setting == 1) {
String err_str;
if (p_errorexp && p_errorexp[0]) {
err_str = p_errorexp;
err_str = String::utf8(p_errorexp);
} else {
err_str = String(p_error);
err_str = String::utf8(p_error);
}
String tooltip_str = String(p_file) + ":" + itos(p_line);
String tooltip_str = String::utf8(p_file) + ":" + itos(p_line);
if (!p_editor_notify) {
if (p_type == ERR_HANDLER_WARNING) {

View file

Before

Width:  |  Height:  |  Size: 495 B

After

Width:  |  Height:  |  Size: 495 B

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c3 0 3-4 6-4s3 4 6 4" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2" transform="translate(0 -1044.4)"/></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c5 0 3-4 6-4s1 4 6 4" fill="none" stroke="#5fb2ff" stroke-linecap="round" stroke-width="2" transform="translate(0 -1044.4)"/></svg>

Before

Width:  |  Height:  |  Size: 231 B

After

Width:  |  Height:  |  Size: 231 B

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 6-4 6 4" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1044.4)"/></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 6-4 6 4" fill="none" stroke="#ffca5f" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" transform="translate(0 -1044.4)"/></svg>

Before

Width:  |  Height:  |  Size: 243 B

After

Width:  |  Height:  |  Size: 243 B

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v6h2v-2.9863-3.0137zm2 3.0137a1.0001 1.0001 0 0 0 .29297.69336l2 2a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-.29297-.29297h3.1719l-.29297.29297a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l2-2a1.0001 1.0001 0 0 0 .29297-.72266 1.0001 1.0001 0 0 0 -.29297-.69141l-2-2a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l.29297.29297h-3.1719l.29297-.29297a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102l-2 2a1.0001 1.0001 0 0 0 -.29297.7207zm10-.029297v3.0156h2v-6h-2z" fill="#e0e0e0"/></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v6h2v-2.9863-3.0137zm2 3.0137a1.0001 1.0001 0 0 0 .29297.69336l2 2a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-.29297-.29297h3.1719l-.29297.29297a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l2-2a1.0001 1.0001 0 0 0 .29297-.72266 1.0001 1.0001 0 0 0 -.29297-.69141l-2-2a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l.29297.29297h-3.1719l.29297-.29297a1 1 0 0 0 0-1.4141 1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102l-2 2a1.0001 1.0001 0 0 0 -.29297.7207zm10-.029297v3.0156h2v-6h-2z" fill="#fc7f7f"/></svg>

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 610 B

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 0-3 2 3 2v-1h3a1 1 0 0 1 1 1 1 1 0 0 1 -1 1v2a3 3 0 0 0 3-3 3 3 0 0 0 -3-3h-3zm-5 1a3 3 0 0 0 -3 3 3 3 0 0 0 3 3h3v1l3-2-3-2v1h-3a1 1 0 0 1 -1-1 1 1 0 0 1 1-1z" fill="#e0e0e0"/></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 0-3 2 3 2v-1h3a1 1 0 0 1 1 1 1 1 0 0 1 -1 1v2a3 3 0 0 0 3-3 3 3 0 0 0 -3-3h-3zm-5 1a3 3 0 0 0 -3 3 3 3 0 0 0 3 3h3v1l3-2-3-2v1h-3a1 1 0 0 1 -1-1 1 1 0 0 1 1-1z" fill="#c38ef1"/></svg>

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

View file

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m10 7h-4v-2l-4 3 4 3v-2h4v2l4-3-4-3z"/><path d="m0 1v14h2v-7-7z"/><path d="m14 1v7 7h2v-14z"/></g></svg>

After

Width:  |  Height:  |  Size: 270 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="b" x1=".26458" x2="3.9688" y1=".79375" y2=".79375" gradientTransform="scale(3.7795)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" offset="0"/><stop stop-color="#ccc" stop-opacity="0" offset="1"/></linearGradient><linearGradient id="a" x1=".26458" x2="3.9688" y1="3.4396" y2="3.4396" gradientTransform="matrix(3.7795 0 0 3.7795 -16 -1.1865e-7)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" offset="0"/><stop stop-color="#ccc" stop-opacity="0" offset="1"/></linearGradient></defs><g><rect x="1" y="1" width="14" height="4" ry="1" fill="url(#b)"/><rect transform="scale(-1,1)" x="-15" y="11" width="14" height="4" ry="1" fill="url(#a)" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.7795"/><path d="m6 6 2 4 2-4z" fill="#ccc"/></g></svg>

After

Width:  |  Height:  |  Size: 952 B

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m2.1665128.99764963c-.422625 0-.763672.34104737-.763672.76367187v4.5742187c0 .4226242.341047.7617192.763672.7617192h4.472656c.422625 0 .763672-.339095.763672-.7617192v-.9882812h-3.300781c-.1662 0-.298828-.3390943-.298828-.7617188v-1.2246094c0-.4226244.132628-.7636718.298828-.7636718h3.300781v-.8359375c0-.4226245-.341047-.76367187-.763672-.76367187z"/><path d="m9.1827441 4.7953408c.5166221-1.0415625 1.0955249-2.2117429 1.2864509-2.600401l.347137-.7066511.679654.00665.679654.00665.956945 2.3125c.526319 1.271875 1.007254 2.4334375 1.068744 2.5812497l.1118.26875h-.597215-.597214l-.332849-.6437497-.332849-.64375h-1.133826-1.133825l-.3786749.6561133-.3786747.6561134-.5922856.000137-.592285.000136zm3.1779349-.369483c.0042-.00346-.233487-.4884588-.528245-1.0777779l-.535922-1.0714891-.03691.0875c-.0203.048125-.183516.425-.362699.8375-.179182.4125-.355738.85125-.392346.975-.03661.12375-.07127.2390723-.07703.2562715-.0083.024853.188215.027989.957503.015278.532385-.0088.971429-.018823.975651-.022283z" stroke="#e0e0e0" stroke-width=".803"/></g></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e1da5b"><path d="m2.1665128.99764963c-.422625 0-.763672.34104737-.763672.76367187v4.5742187c0 .4226242.341047.7617192.763672.7617192h4.472656c.422625 0 .763672-.339095.763672-.7617192v-.9882812h-3.300781c-.1662 0-.298828-.3390943-.298828-.7617188v-1.2246094c0-.4226244.132628-.7636718.298828-.7636718h3.300781v-.8359375c0-.4226245-.341047-.76367187-.763672-.76367187z"/><path d="m9.1827441 4.7953408c.5166221-1.0415625 1.0955249-2.2117429 1.2864509-2.600401l.347137-.7066511.679654.00665.679654.00665.956945 2.3125c.526319 1.271875 1.007254 2.4334375 1.068744 2.5812497l.1118.26875h-.597215-.597214l-.332849-.6437497-.332849-.64375h-1.133826-1.133825l-.3786749.6561133-.3786747.6561134-.5922856.000137-.592285.000136zm3.1779349-.369483c.0042-.00346-.233487-.4884588-.528245-1.0777779l-.535922-1.0714891-.03691.0875c-.0203.048125-.183516.425-.362699.8375-.179182.4125-.355738.85125-.392346.975-.03661.12375-.07127.2390723-.07703.2562715-.0083.024853.188215.027989.957503.015278.532385-.0088.971429-.018823.975651-.022283z" stroke="#e1da5b" stroke-width=".803"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 1a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-6 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-6 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1z" fill="#e0e0e0"/></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m14 1a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-6 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-6 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1z" fill="#b9ec41"/></svg>

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

View file

@ -1 +1 @@
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v2h2v4h2v-4h2v-2zm13 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1z" fill="#e0e0e0"/></svg>
<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v2h2v4h2v-4h2v-2zm13 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3 2a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1z" fill="#f68f45"/></svg>

Before

Width:  |  Height:  |  Size: 328 B

After

Width:  |  Height:  |  Size: 328 B

View file

@ -57,6 +57,7 @@ void EditorImportPlugin::get_recognized_extensions(List<String> *p_extensions) c
for (int i = 0; i < extensions.size(); i++) {
p_extensions->push_back(extensions[i]);
}
return;
}
ERR_FAIL_MSG("Unimplemented _get_recognized_extensions in add-on.");
}
@ -139,6 +140,7 @@ void EditorImportPlugin::get_import_options(List<ResourceImporter::ImportOption>
ImportOption option(PropertyInfo(default_value.get_type(), name, hint, hint_string, usage), default_value);
r_options->push_back(option);
}
return;
}
ERR_FAIL_MSG("Unimplemented _get_import_options in add-on.");

View file

@ -359,6 +359,8 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
int height = 0;
int ascent = 0;
int outline = 0;
uint32_t st_flags = 0;
String font_name;
bool packed = false;
uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
@ -382,13 +384,23 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
base_size = f->get_16();
uint8_t flags = f->get_8();
ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
if (flags & (1 << 3)) {
st_flags |= TextServer::FONT_BOLD;
}
if (flags & (1 << 2)) {
st_flags |= TextServer::FONT_ITALIC;
}
f->get_8(); // non-unicode charset, skip
f->get_16(); // stretch_h, skip
f->get_8(); // aa, skip
f->get_32(); // padding, skip
f->get_16(); // spacing, skip
outline = f->get_8();
// font name, skip
// font name
PackedByteArray name_data;
name_data.resize(block_size - 14);
f->get_buffer(name_data.ptrw(), block_size - 14);
font_name = String::utf8((const char *)name_data.ptr(), block_size - 14);
font->set_fixed_size(base_size);
} break;
case 2: /* common */ {
@ -601,6 +613,19 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
if (keys.has("outline")) {
outline = keys["outline"].to_int();
}
if (keys.has("bold")) {
if (keys["bold"].to_int()) {
st_flags |= TextServer::FONT_BOLD;
}
}
if (keys.has("italic")) {
if (keys["italic"].to_int()) {
st_flags |= TextServer::FONT_ITALIC;
}
}
if (keys.has("face")) {
font_name = keys["face"];
}
ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
} else if (type == "common") {
if (keys.has("lineHeight")) {
@ -778,6 +803,8 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
}
}
font->set_font_name(font_name);
font->set_font_style(st_flags);
font->set_ascent(0, base_size, ascent);
font->set_descent(0, base_size, height - ascent);

View file

@ -405,11 +405,11 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
current_material_library = l.replace("mtllib", "").strip_edges();
if (!material_map.has(current_material_library)) {
Map<String, Ref<StandardMaterial3D>> lib;
Error err = _parse_material_library(current_material_library, lib, r_missing_deps);
if (err == ERR_CANT_OPEN) {
String dir = p_path.get_base_dir();
err = _parse_material_library(dir.plus_file(current_material_library), lib, r_missing_deps);
String lib_path = current_material_library;
if (lib_path.is_relative_path()) {
lib_path = p_path.get_base_dir().plus_file(current_material_library);
}
Error err = _parse_material_library(lib_path, lib, r_missing_deps);
if (err == OK) {
material_map[current_material_library] = lib;
}

View file

@ -424,10 +424,10 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
String animname = E;
const int loop_string_count = 3;
static const char *loop_strings[loop_string_count] = { "loops", "loop", "cycle" };
static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };
for (int i = 0; i < loop_string_count; i++) {
if (_teststr(animname, loop_strings[i])) {
anim->set_loop(true);
anim->set_loop_mode(Animation::LoopMode::LOOP_LINEAR);
animname = _fixstr(animname, loop_strings[i]);
ap->rename_animation(E, animname);
}
@ -868,7 +868,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
String name = node_settings["clip_" + itos(i + 1) + "/name"];
int from_frame = node_settings["clip_" + itos(i + 1) + "/start_frame"];
int end_frame = node_settings["clip_" + itos(i + 1) + "/end_frame"];
bool loop = node_settings["clip_" + itos(i + 1) + "/loops"];
Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)node_settings["clip_" + itos(i + 1) + "/loop_mode"]);
bool save_to_file = node_settings["clip_" + itos(i + 1) + "/save_to_file/enabled"];
bool save_to_path = node_settings["clip_" + itos(i + 1) + "/save_to_file/path"];
bool save_to_file_keep_custom = node_settings["clip_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"];
@ -876,7 +876,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
animation_clips.push_back(name);
animation_clips.push_back(from_frame / p_animation_fps);
animation_clips.push_back(end_frame / p_animation_fps);
animation_clips.push_back(loop);
animation_clips.push_back(loop_mode);
animation_clips.push_back(save_to_file);
animation_clips.push_back(save_to_path);
animation_clips.push_back(save_to_file_keep_custom);
@ -903,7 +903,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
}
anim->set_loop(anim_settings["settings/loops"]);
anim->set_loop_mode(static_cast<Animation::LoopMode>((int)anim_settings["settings/loop_mode"]));
bool save = anim_settings["save_to_file/enabled"];
String path = anim_settings["save_to_file/path"];
bool keep_custom = anim_settings["save_to_file/keep_custom_tracks"];
@ -977,7 +977,7 @@ Ref<Animation> ResourceImporterScene::_save_animation_to_file(Ref<Animation> ani
old_anim->copy_track(i, anim);
}
}
anim->set_loop(old_anim->has_loop());
anim->set_loop_mode(old_anim->get_loop_mode());
}
}
@ -1005,7 +1005,7 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
String name = p_clips[i];
float from = p_clips[i + 1];
float to = p_clips[i + 2];
bool loop = p_clips[i + 3];
Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)p_clips[i + 3]);
bool save_to_file = p_clips[i + 4];
String save_to_path = p_clips[i + 5];
bool keep_current = p_clips[i + 6];
@ -1135,7 +1135,7 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
}
}
new_anim->set_loop(loop);
new_anim->set_loop_mode(loop_mode);
new_anim->set_length(to - from);
anim->add_animation(name, new_anim);
@ -1218,7 +1218,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/path", PROPERTY_HINT_FILE, "*.material,*.res,*.tres"), ""));
} break;
case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "settings/loops"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "settings/loop_mode"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/keep_custom_tracks"), ""));
@ -1240,7 +1240,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/name"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/start_frame"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/end_frame"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/loops"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/loop_mode"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/path", PROPERTY_HINT_SAVE_FILE, ".res,*.tres"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));
@ -1428,7 +1428,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary()));

View file

@ -272,7 +272,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
if (loop_type == 0x00) {
loop = AudioStreamSample::LOOP_FORWARD;
} else if (loop_type == 0x01) {
loop = AudioStreamSample::LOOP_PING_PONG;
loop = AudioStreamSample::LOOP_PINGPONG;
} else if (loop_type == 0x02) {
loop = AudioStreamSample::LOOP_BACKWARD;
}

View file

@ -41,6 +41,7 @@
#include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
#include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning.
#include "scene/main/window.h"
#include "scene/resources/animation.h"
#include "servers/rendering_server.h"
void AnimationPlayerEditor::_node_removed(Node *p_node) {
@ -72,7 +73,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
if (player->has_animation(animname)) {
Ref<Animation> anim = player->get_animation(animname);
if (!anim.is_null()) {
frame->set_max(anim->get_length());
frame->set_max((double)anim->get_length());
}
}
}
@ -289,7 +290,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
track_editor->set_root(root);
}
}
frame->set_max(anim->get_length());
frame->set_max((double)anim->get_length());
} else {
track_editor->set_animation(Ref<Animation>());
@ -1014,7 +1015,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool
Ref<Animation> anim;
anim = player->get_animation(current);
float pos = CLAMP(anim->get_length() * (p_value / frame->get_max()), 0, anim->get_length());
float pos = CLAMP((double)anim->get_length() * (p_value / frame->get_max()), 0, (double)anim->get_length());
if (track_editor->is_snap_enabled()) {
pos = Math::snapped(pos, _get_editor_step());
}
@ -1424,7 +1425,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
float pos = cpos + step_off * anim->get_step();
bool valid = anim->has_loop() || (pos >= 0 && pos <= anim->get_length());
bool valid = anim->get_loop_mode() != Animation::LoopMode::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
onion.captures_valid.write[cidx] = valid;
if (valid) {
player->seek(pos, true);

View file

@ -2264,9 +2264,9 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
}
if (b.is_valid() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_ctrl_pressed()) {
add_node_menu->set_position(get_global_transform().xform(get_local_mouse_position()));
if (b.is_valid() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT) {
add_node_menu->set_size(Vector2(1, 1));
add_node_menu->set_position(get_screen_position() + b->get_position());
add_node_menu->popup();
node_create_position = transform.affine_inverse().xform((get_local_mouse_position()));
return true;
@ -6192,14 +6192,14 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
label = memnew(Label);
label->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 1));
label->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE);
label->add_theme_constant_override("shadow_outline_size", 1 * EDSCALE);
label->hide();
canvas_item_editor->get_controls_container()->add_child(label);
label_desc = memnew(Label);
label_desc->add_theme_color_override("font_color", Color(0.6f, 0.6f, 0.6f, 1));
label_desc->add_theme_color_override("font_shadow_color", Color(0.2f, 0.2f, 0.2f, 1));
label_desc->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE);
label_desc->add_theme_constant_override("shadow_outline_size", 1 * EDSCALE);
label_desc->add_theme_constant_override("line_spacing", 0);
label_desc->hide();
canvas_item_editor->get_controls_container()->add_child(label_desc);

View file

@ -297,12 +297,14 @@ EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() {
//////////////////////////////////////////////////////////////////
void EditorMaterialPreviewPlugin::_preview_done(const Variant &p_udata) {
preview_done.set();
void EditorMaterialPreviewPlugin::_generate_frame_started() {
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorMaterialPreviewPlugin *>(this), &EditorMaterialPreviewPlugin::_preview_done));
}
void EditorMaterialPreviewPlugin::_bind_methods() {
ClassDB::bind_method("_preview_done", &EditorMaterialPreviewPlugin::_preview_done);
void EditorMaterialPreviewPlugin::_preview_done() {
preview_done.post();
}
bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
@ -320,14 +322,9 @@ Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Si
if (material->get_shader_mode() == Shader::MODE_SPATIAL) {
RS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<EditorMaterialPreviewPlugin *>(this), &EditorMaterialPreviewPlugin::_generate_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
preview_done.clear();
RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant());
while (!preview_done.is_set()) {
OS::get_singleton()->delay_usec(10);
}
preview_done.wait();
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
RS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
@ -699,12 +696,14 @@ EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() {
///////////////////////////////////////////////////////////////////////////
void EditorMeshPreviewPlugin::_preview_done(const Variant &p_udata) {
preview_done.set();
void EditorMeshPreviewPlugin::_generate_frame_started() {
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorMeshPreviewPlugin *>(this), &EditorMeshPreviewPlugin::_preview_done));
}
void EditorMeshPreviewPlugin::_bind_methods() {
ClassDB::bind_method("_preview_done", &EditorMeshPreviewPlugin::_preview_done);
void EditorMeshPreviewPlugin::_preview_done() {
preview_done.post();
}
bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
@ -735,14 +734,9 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2
xform.origin.z -= rot_aabb.size.z * 2;
RS::get_singleton()->instance_set_transform(mesh_instance, xform);
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<EditorMeshPreviewPlugin *>(this), &EditorMeshPreviewPlugin::_generate_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
preview_done.clear();
RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant());
while (!preview_done.is_set()) {
OS::get_singleton()->delay_usec(10);
}
preview_done.wait();
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
@ -814,12 +808,14 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
///////////////////////////////////////////////////////////////////////////
void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) {
preview_done.set();
void EditorFontPreviewPlugin::_generate_frame_started() {
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorFontPreviewPlugin *>(this), &EditorFontPreviewPlugin::_preview_done));
}
void EditorFontPreviewPlugin::_bind_methods() {
ClassDB::bind_method("_preview_done", &EditorFontPreviewPlugin::_preview_done);
void EditorFontPreviewPlugin::_preview_done() {
preview_done.post();
}
bool EditorFontPreviewPlugin::handles(const String &p_type) const {
@ -857,13 +853,9 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path,
font->draw_string(canvas_item, pos, sample, HALIGN_LEFT, -1.f, 50, Color(1, 1, 1));
preview_done.clear();
RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant());
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<EditorFontPreviewPlugin *>(this), &EditorFontPreviewPlugin::_generate_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
while (!preview_done.is_set()) {
OS::get_singleton()->delay_usec(10);
}
preview_done.wait();
RS::get_singleton()->canvas_item_clear(canvas_item);

View file

@ -92,12 +92,10 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
RID light2;
RID light_instance2;
RID camera;
mutable SafeFlag preview_done;
Semaphore preview_done;
void _preview_done(const Variant &p_udata);
protected:
static void _bind_methods();
void _generate_frame_started();
void _preview_done();
public:
virtual bool handles(const String &p_type) const override;
@ -136,12 +134,10 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
RID light2;
RID light_instance2;
RID camera;
mutable SafeFlag preview_done;
Semaphore preview_done;
void _preview_done(const Variant &p_udata);
protected:
static void _bind_methods();
void _generate_frame_started();
void _preview_done();
public:
virtual bool handles(const String &p_type) const override;
@ -158,12 +154,10 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
RID viewport_texture;
RID canvas;
RID canvas_item;
mutable SafeFlag preview_done;
Semaphore preview_done;
void _preview_done(const Variant &p_udata);
protected:
static void _bind_methods();
void _generate_frame_started();
void _preview_done();
public:
virtual bool handles(const String &p_type) const override;
@ -177,12 +171,10 @@ public:
class EditorTileMapPatternPreviewPlugin : public EditorResourcePreviewGenerator {
GDCLASS(EditorTileMapPatternPreviewPlugin, EditorResourcePreviewGenerator);
mutable SafeFlag preview_done;
Semaphore preview_done;
void _preview_done(const Variant &p_udata);
protected:
static void _bind_methods();
void _generate_frame_started();
void _preview_done();
public:
virtual bool handles(const String &p_type) const override;

View file

@ -46,6 +46,8 @@ void GradientEditor::_gradient_changed() {
editing = true;
Vector<Gradient::Point> points = gradient->get_points();
set_points(points);
set_interpolation_mode(gradient->get_interpolation_mode());
update();
editing = false;
}
@ -55,8 +57,10 @@ void GradientEditor::_ramp_changed() {
undo_redo->create_action(TTR("Gradient Edited"));
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode());
undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());
undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors());
undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode());
undo_redo->commit_action();
editing = false;
}
@ -69,6 +73,14 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed));
gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed));
set_points(gradient->get_points());
set_interpolation_mode(gradient->get_interpolation_mode());
}
void GradientEditor::reverse_gradient() {
gradient->reverse();
set_points(gradient->get_points());
emit_signal(SNAME("ramp_changed"));
update();
}
GradientEditor::GradientEditor() {
@ -77,6 +89,23 @@ GradientEditor::GradientEditor() {
///////////////////////
void GradientReverseButton::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
Ref<Texture2D> icon = get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"));
if (is_pressed()) {
draw_texture_rect(icon, Rect2(margin, margin, icon->get_width(), icon->get_height()), false, get_theme_color(SNAME("icon_pressed_color"), SNAME("Button")));
} else {
draw_texture_rect(icon, Rect2(margin, margin, icon->get_width(), icon->get_height()));
}
}
}
Size2 GradientReverseButton::get_minimum_size() const {
return (get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"))->get_size() + Size2(margin * 2, margin * 2));
}
///////////////////////
bool EditorInspectorPluginGradient::can_handle(Object *p_object) {
return Object::cast_to<Gradient>(p_object) != nullptr;
}
@ -85,9 +114,23 @@ void EditorInspectorPluginGradient::parse_begin(Object *p_object) {
Gradient *gradient = Object::cast_to<Gradient>(p_object);
Ref<Gradient> g(gradient);
GradientEditor *editor = memnew(GradientEditor);
editor = memnew(GradientEditor);
editor->set_gradient(g);
add_custom_control(editor);
reverse_btn = memnew(GradientReverseButton);
gradient_tools_hbox = memnew(HBoxContainer);
gradient_tools_hbox->add_child(reverse_btn);
add_custom_control(gradient_tools_hbox);
reverse_btn->connect("pressed", callable_mp(this, &EditorInspectorPluginGradient::_reverse_button_pressed));
reverse_btn->set_tooltip(TTR("Reverse/mirror gradient."));
}
void EditorInspectorPluginGradient::_reverse_button_pressed() {
editor->reverse_gradient();
}
GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) {

View file

@ -50,12 +50,28 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
void set_gradient(const Ref<Gradient> &p_gradient);
void reverse_gradient();
GradientEditor();
};
class GradientReverseButton : public BaseButton {
GDCLASS(GradientReverseButton, BaseButton);
int margin = 2;
void _notification(int p_what);
virtual Size2 get_minimum_size() const override;
};
class EditorInspectorPluginGradient : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginGradient, EditorInspectorPlugin);
GradientEditor *editor;
HBoxContainer *gradient_tools_hbox;
GradientReverseButton *reverse_btn;
void _reverse_button_pressed();
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;

View file

@ -2245,12 +2245,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_menu_option(VIEW_RIGHT);
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) {
cursor.x_rot -= Math_PI / 12.0;
// Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
cursor.x_rot = CLAMP(cursor.x_rot - Math_PI / 12.0, -1.57, 1.57);
view_type = VIEW_TYPE_USER;
_update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_up", p_event)) {
cursor.x_rot += Math_PI / 12.0;
// Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
cursor.x_rot = CLAMP(cursor.x_rot + Math_PI / 12.0, -1.57, 1.57);
view_type = VIEW_TYPE_USER;
_update_name();
}
@ -2885,13 +2887,13 @@ void Node3DEditorViewport::_notification(int p_what) {
// Color labels depending on performance level ("good" = green, "OK" = yellow, "bad" = red).
// Middle point is at 15 ms.
cpu_time_label->set_text(vformat(TTR("CPU Time: %s ms"), rtos(cpu_time).pad_decimals(1)));
cpu_time_label->set_text(vformat(TTR("CPU Time: %s ms"), rtos(cpu_time).pad_decimals(2)));
cpu_time_label->add_theme_color_override(
"font_color",
frame_time_gradient->get_color_at_offset(
Math::range_lerp(cpu_time, 0, 30, 0, 1)));
gpu_time_label->set_text(vformat(TTR("GPU Time: %s ms"), rtos(gpu_time).pad_decimals(1)));
gpu_time_label->set_text(vformat(TTR("GPU Time: %s ms"), rtos(gpu_time).pad_decimals(2)));
// Middle point is at 15 ms.
gpu_time_label->add_theme_color_override(
"font_color",
@ -3936,9 +3938,13 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
Vector3 point = world_pos + world_ray * MAX_DISTANCE;
PhysicsDirectSpaceState3D *ss = get_tree()->get_root()->get_world_3d()->get_direct_space_state();
PhysicsDirectSpaceState3D::RayResult result;
if (ss->intersect_ray(world_pos, world_pos + world_ray * MAX_DISTANCE, result)) {
PhysicsDirectSpaceState3D::RayParameters ray_params;
ray_params.from = world_pos;
ray_params.to = world_pos + world_ray * MAX_DISTANCE;
PhysicsDirectSpaceState3D::RayResult result;
if (ss->intersect_ray(ray_params, result)) {
point = result.position;
}
@ -6566,7 +6572,12 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Vector3 to = from - Vector3(0.0, max_snap_height, 0.0);
Set<RID> excluded = _get_physics_bodies_rid(sp);
if (ss->intersect_ray(from, to, result, excluded)) {
PhysicsDirectSpaceState3D::RayParameters ray_params;
ray_params.from = from;
ray_params.to = to;
ray_params.exclude = excluded;
if (ss->intersect_ray(ray_params, result)) {
snapped_to_floor = true;
}
}
@ -6583,7 +6594,12 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Vector3 to = from - Vector3(0.0, max_snap_height, 0.0);
Set<RID> excluded = _get_physics_bodies_rid(sp);
if (ss->intersect_ray(from, to, result, excluded)) {
PhysicsDirectSpaceState3D::RayParameters ray_params;
ray_params.from = from;
ray_params.to = to;
ray_params.exclude = excluded;
if (ss->intersect_ray(ray_params, result)) {
Vector3 position_offset = d["position_offset"];
Transform3D new_transform = sp->get_global_transform();
@ -6636,7 +6652,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
Node *new_sun = preview_sun->duplicate();
undo_redo->create_action(TTR("Add Preview Sun to Scene"));
undo_redo->add_do_method(base, "add_child", new_sun);
undo_redo->add_do_method(base, "add_child", new_sun, true);
// Move to the beginning of the scene tree since more "global" nodes
// generally look better when placed at the top.
undo_redo->add_do_method(base, "move_child", new_sun, 0);
@ -6666,7 +6682,7 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) {
new_env->set_environment(preview_environment->get_environment()->duplicate(true));
undo_redo->create_action(TTR("Add Preview Environment to Scene"));
undo_redo->add_do_method(base, "add_child", new_env);
undo_redo->add_do_method(base, "add_child", new_env, true);
// Move to the beginning of the scene tree since more "global" nodes
// generally look better when placed at the top.
undo_redo->add_do_method(base, "move_child", new_env, 0);
@ -7132,7 +7148,7 @@ void Node3DEditor::_update_preview_environment() {
} else {
if (!preview_sun->get_parent()) {
add_child(preview_sun);
add_child(preview_sun, true);
sun_state->hide();
sun_vb->show();
}

View file

@ -989,10 +989,6 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
RES script = se->get_edited_resource();
if (script->is_built_in()) {
continue; //internal script, who cares
}
if (script == p_res) {
se->tag_saved_version();
}
@ -1002,6 +998,31 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
_trigger_live_script_reload();
}
void ScriptEditor::_scene_saved_callback(const String &p_path) {
// If scene was saved, mark all built-in scripts from that scene as saved.
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (!se) {
continue;
}
RES edited_res = se->get_edited_resource();
if (!edited_res->is_built_in()) {
continue; // External script, who cares.
}
if (edited_res->get_path().get_slice("::", 0) == p_path) {
se->tag_saved_version();
}
Ref<Script> scr = edited_res;
if (scr.is_valid() && scr->is_tool()) {
scr->reload(true);
}
}
}
void ScriptEditor::_trigger_live_script_reload() {
if (!pending_auto_reload && auto_reload_running_scripts) {
call_deferred(SNAME("_live_auto_reload_running_scripts"));
@ -1525,6 +1546,7 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
editor->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
editor->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
editor->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
editor->get_filesystem_dock()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
editor->get_filesystem_dock()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
@ -1619,7 +1641,7 @@ void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
}
if (script->is_built_in() && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
_close_tab(i);
_close_tab(i, false);
i--;
}
}
@ -1926,20 +1948,7 @@ void ScriptEditor::_update_script_names() {
// to update original path to previously edited resource.
se->set_meta("_edit_res_path", path);
}
bool built_in = !path.is_resource_file();
String name;
if (built_in) {
name = path.get_file();
const String &resource_name = se->get_edited_resource()->get_name();
if (resource_name != "") {
// If the built-in script has a custom resource name defined,
// display the built-in script name as follows: `ResourceName (scene_file.tscn)`
name = vformat("%s (%s)", resource_name, name.substr(0, name.find("::", 0)));
}
} else {
name = se->get_name();
}
String name = se->get_name();
_ScriptEditorItemData sd;
sd.icon = icon;
@ -2403,7 +2412,17 @@ void ScriptEditor::save_current_script() {
}
}
editor->save_resource(resource);
if (resource->is_built_in()) {
// If built-in script, save the scene instead.
const String scene_path = resource->get_path().get_slice("::", 0);
if (!scene_path.is_empty()) {
Vector<String> scene_to_save;
scene_to_save.push_back(scene_path);
editor->save_scene_list(scene_to_save);
}
} else {
editor->save_resource(resource);
}
if (script != nullptr) {
const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
@ -2416,6 +2435,8 @@ void ScriptEditor::save_current_script() {
}
void ScriptEditor::save_all_scripts() {
Vector<String> scenes_to_save;
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (!se) {
@ -2474,9 +2495,19 @@ void ScriptEditor::save_all_scripts() {
update_doc(doc.name);
}
}
} else {
// For built-in scripts, save their scenes instead.
const String scene_path = edited_res->get_path().get_slice("::", 0);
if (!scenes_to_save.has(scene_path)) {
scenes_to_save.push_back(scene_path);
}
}
}
if (!scenes_to_save.is_empty()) {
editor->save_scene_list(scenes_to_save);
}
_update_script_names();
EditorFileSystem::get_singleton()->update_script_classes();
}

View file

@ -365,6 +365,7 @@ class ScriptEditor : public PanelContainer {
void _add_callback(Object *p_obj, const String &p_function, const PackedStringArray &p_args);
void _res_saved_callback(const Ref<Resource> &p_res);
void _scene_saved_callback(const String &p_path);
bool open_textfile_after_create = true;
bool trim_trailing_whitespace_on_save;

View file

@ -375,18 +375,21 @@ void ScriptTextEditor::ensure_focus() {
String ScriptTextEditor::get_name() {
String name;
if (!script->is_built_in()) {
name = script->get_path().get_file();
if (is_unsaved()) {
if (script->get_path().is_empty()) {
name = TTR("[unsaved]");
}
name += "(*)";
name = script->get_path().get_file();
if (name.is_empty()) {
// This appears for newly created built-in scripts before saving the scene.
name = TTR("[unsaved]");
} else if (script->is_built_in()) {
const String &script_name = script->get_name();
if (script_name != "") {
// If the built-in script has a custom resource name defined,
// display the built-in script name as follows: `ResourceName (scene_file.tscn)`
name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
}
} else if (script->get_name() != "") {
name = script->get_name();
} else {
name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
}
if (is_unsaved()) {
name += "(*)";
}
return name;

View file

@ -0,0 +1,375 @@
/*************************************************************************/
/* text_control_editor_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "text_control_editor_plugin.h"
#include "editor/editor_scale.h"
void TextControlEditor::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ENTER_TREE: {
if (!EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts))) {
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts), make_binds(""));
}
[[fallthrough]];
}
case NOTIFICATION_THEME_CHANGED: {
clear_formatting->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
} break;
case NOTIFICATION_EXIT_TREE: {
if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts))) {
EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts));
}
} break;
default:
break;
}
}
void TextControlEditor::_find_resources(EditorFileSystemDirectory *p_dir) {
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
_find_resources(p_dir->get_subdir(i));
}
for (int i = 0; i < p_dir->get_file_count(); i++) {
if (p_dir->get_file_type(i) == "FontData") {
Ref<FontData> fd = ResourceLoader::load(p_dir->get_file_path(i));
if (fd.is_valid()) {
String name = fd->get_font_name();
String sty = fd->get_font_style_name();
if (sty.is_empty()) {
sty = "Default";
}
fonts[name][sty] = p_dir->get_file_path(i);
}
}
}
}
void TextControlEditor::_reload_fonts(const String &p_path) {
fonts.clear();
_find_resources(EditorFileSystem::get_singleton()->get_filesystem());
_update_control();
}
void TextControlEditor::_update_fonts_menu() {
font_list->clear();
font_list->add_item(TTR("[Theme Default]"), FONT_INFO_THEME_DEFAULT);
if (custom_font.is_valid()) {
font_list->add_item(TTR("[Custom Font]"), FONT_INFO_USER_CUSTOM);
}
int id = FONT_INFO_ID;
for (Map<String, Map<String, String>>::Element *E = fonts.front(); E; E = E->next()) {
font_list->add_item(E->key(), id++);
}
if (font_list->get_item_count() > 1) {
font_list->show();
} else {
font_list->hide();
}
}
void TextControlEditor::_update_styles_menu() {
font_style_list->clear();
if ((font_list->get_selected_id() >= FONT_INFO_ID)) {
const String &name = font_list->get_item_text(font_list->get_selected());
for (Map<String, String>::Element *E = fonts[name].front(); E; E = E->next()) {
font_style_list->add_item(E->key());
}
} else {
font_style_list->add_item("Default");
}
if (font_style_list->get_item_count() > 1) {
font_style_list->show();
} else {
font_style_list->hide();
}
}
void TextControlEditor::_update_control() {
if (edited_control) {
// Get override names.
if (edited_control->is_class("RichTextLabel")) {
edited_color = "default_color";
edited_font = "normal_font";
edited_font_size = "normal_font_size";
} else {
edited_color = "font_color";
edited_font = "font";
edited_font_size = "font_size";
}
// Get font override.
Ref<Font> font;
if (edited_control->has_theme_font_override(edited_font)) {
font = edited_control->get_theme_font(edited_font);
}
if (font.is_valid()) {
if (font->get_data_count() != 1) {
// Composite font, save it to "custom_font" to allow undoing font change.
custom_font = font;
_update_fonts_menu();
font_list->select(FONT_INFO_USER_CUSTOM);
_update_styles_menu();
font_style_list->select(0);
} else {
// Single face font, search for the font with matching name and style.
String name = font->get_data(0)->get_font_name();
String style = font->get_data(0)->get_font_style_name();
if (fonts.has(name) && fonts[name].has(style)) {
_update_fonts_menu();
for (int i = 0; i < font_list->get_item_count(); i++) {
if (font_list->get_item_text(i) == name) {
font_list->select(i);
break;
}
}
_update_styles_menu();
for (int i = 0; i < font_style_list->get_item_count(); i++) {
if (font_style_list->get_item_text(i) == style) {
font_style_list->select(i);
break;
}
}
} else {
// Unknown font, save it to "custom_font" to allow undoing font change.
custom_font = font;
_update_fonts_menu();
font_list->select(FONT_INFO_USER_CUSTOM);
_update_styles_menu();
font_style_list->select(0);
}
}
} else {
// No font override, select "Theme Default".
_update_fonts_menu();
font_list->select(FONT_INFO_THEME_DEFAULT);
_update_styles_menu();
font_style_list->select(0);
}
// Get other theme overrides.
font_size_list->set_value(edited_control->get_theme_font_size(edited_font_size));
outline_size_list->set_value(edited_control->get_theme_constant("outline_size"));
font_color_picker->set_pick_color(edited_control->get_theme_color(edited_color));
outline_color_picker->set_pick_color(edited_control->get_theme_color("font_outline_color"));
}
}
void TextControlEditor::_font_selected(int p_id) {
_update_styles_menu();
_set_font();
}
void TextControlEditor::_font_style_selected(int p_id) {
_set_font();
}
void TextControlEditor::_set_font() {
if (edited_control) {
if (font_list->get_selected_id() == FONT_INFO_THEME_DEFAULT) {
// Remove font override.
edited_control->remove_theme_font_override(edited_font);
return;
} else if (font_list->get_selected_id() == FONT_INFO_USER_CUSTOM) {
// Restore "custom_font".
edited_control->add_theme_font_override(edited_font, custom_font);
return;
} else {
// Load new font resource using selected name and style.
String name = font_list->get_item_text(font_list->get_selected());
String sty = font_style_list->get_item_text(font_style_list->get_selected());
if (sty.is_empty()) {
sty = "Default";
}
if (fonts.has(name)) {
Ref<FontData> fd = ResourceLoader::load(fonts[name][sty]);
if (fd.is_valid()) {
Ref<Font> f;
f.instantiate();
f->add_data(fd);
edited_control->add_theme_font_override(edited_font, f);
}
}
}
}
}
void TextControlEditor::_font_size_selected(double p_size) {
if (edited_control) {
edited_control->add_theme_font_size_override(edited_font_size, p_size);
}
}
void TextControlEditor::_outline_size_selected(double p_size) {
if (edited_control) {
edited_control->add_theme_constant_override("outline_size", p_size);
}
}
void TextControlEditor::_font_color_changed(const Color &p_color) {
if (edited_control) {
edited_control->add_theme_color_override(edited_color, p_color);
}
}
void TextControlEditor::_outline_color_changed(const Color &p_color) {
if (edited_control) {
edited_control->add_theme_color_override("font_outline_color", p_color);
}
}
void TextControlEditor::_clear_formatting() {
if (edited_control) {
edited_control->begin_bulk_theme_override();
edited_control->remove_theme_font_override(edited_font);
edited_control->remove_theme_font_size_override(edited_font_size);
edited_control->remove_theme_color_override(edited_color);
edited_control->remove_theme_color_override("font_outline_color");
edited_control->remove_theme_constant_override("outline_size");
edited_control->end_bulk_theme_override();
_update_control();
}
}
void TextControlEditor::edit(Object *p_object) {
Control *ctrl = Object::cast_to<Control>(p_object);
if (!ctrl) {
edited_control = nullptr;
custom_font = Ref<Font>();
} else {
edited_control = ctrl;
custom_font = Ref<Font>();
_update_control();
}
}
bool TextControlEditor::handles(Object *p_object) const {
Control *ctrl = Object::cast_to<Control>(p_object);
if (!ctrl) {
return false;
} else {
bool valid = false;
ctrl->get("text", &valid);
return valid;
}
}
TextControlEditor::TextControlEditor() {
add_child(memnew(VSeparator));
font_list = memnew(OptionButton);
font_list->set_flat(true);
font_list->set_tooltip(TTR("Font"));
add_child(font_list);
font_list->connect("item_selected", callable_mp(this, &TextControlEditor::_font_selected));
font_style_list = memnew(OptionButton);
font_style_list->set_flat(true);
font_style_list->set_tooltip(TTR("Font style"));
font_style_list->set_toggle_mode(true);
add_child(font_style_list);
font_style_list->connect("item_selected", callable_mp(this, &TextControlEditor::_font_style_selected));
font_size_list = memnew(SpinBox);
font_size_list->set_tooltip(TTR("Font Size"));
font_size_list->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
font_size_list->set_min(6);
font_size_list->set_step(1);
font_size_list->set_max(96);
font_size_list->get_line_edit()->set_flat(true);
add_child(font_size_list);
font_size_list->connect("value_changed", callable_mp(this, &TextControlEditor::_font_size_selected));
font_color_picker = memnew(ColorPickerButton);
font_color_picker->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
font_color_picker->set_flat(true);
font_color_picker->set_tooltip(TTR("Text Color"));
add_child(font_color_picker);
font_color_picker->connect("color_changed", callable_mp(this, &TextControlEditor::_font_color_changed));
add_child(memnew(VSeparator));
outline_size_list = memnew(SpinBox);
outline_size_list->set_tooltip(TTR("Outline Size"));
outline_size_list->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
outline_size_list->set_min(0);
outline_size_list->set_step(1);
outline_size_list->set_max(96);
outline_size_list->get_line_edit()->set_flat(true);
add_child(outline_size_list);
outline_size_list->connect("value_changed", callable_mp(this, &TextControlEditor::_outline_size_selected));
outline_color_picker = memnew(ColorPickerButton);
outline_color_picker->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
outline_color_picker->set_flat(true);
outline_color_picker->set_tooltip(TTR("Outline Color"));
add_child(outline_color_picker);
outline_color_picker->connect("color_changed", callable_mp(this, &TextControlEditor::_outline_color_changed));
add_child(memnew(VSeparator));
clear_formatting = memnew(Button);
clear_formatting->set_flat(true);
clear_formatting->set_tooltip(TTR("Clear Formatting"));
add_child(clear_formatting);
clear_formatting->connect("pressed", callable_mp(this, &TextControlEditor::_clear_formatting));
}
/*************************************************************************/
void TextControlEditorPlugin::edit(Object *p_object) {
text_ctl_editor->edit(p_object);
}
bool TextControlEditorPlugin::handles(Object *p_object) const {
return text_ctl_editor->handles(p_object);
}
void TextControlEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
text_ctl_editor->show();
} else {
text_ctl_editor->hide();
text_ctl_editor->edit(nullptr);
}
}
TextControlEditorPlugin::TextControlEditorPlugin(EditorNode *p_node) {
editor = p_node;
text_ctl_editor = memnew(TextControlEditor);
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(text_ctl_editor);
text_ctl_editor->hide();
}

View file

@ -0,0 +1,119 @@
/*************************************************************************/
/* text_control_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef TEXT_CONTROL_EDITOR_PLUGIN_H
#define TEXT_CONTROL_EDITOR_PLUGIN_H
#include "canvas_item_editor_plugin.h"
#include "editor/editor_file_system.h"
#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/popup_menu.h"
/*************************************************************************/
class TextControlEditor : public HBoxContainer {
GDCLASS(TextControlEditor, HBoxContainer);
enum FontInfoID {
FONT_INFO_THEME_DEFAULT = 0,
FONT_INFO_USER_CUSTOM = 1,
FONT_INFO_ID = 100,
};
Map<String, Map<String, String>> fonts;
OptionButton *font_list = nullptr;
SpinBox *font_size_list = nullptr;
OptionButton *font_style_list = nullptr;
ColorPickerButton *font_color_picker = nullptr;
SpinBox *outline_size_list = nullptr;
ColorPickerButton *outline_color_picker = nullptr;
Button *clear_formatting = nullptr;
Control *edited_control = nullptr;
String edited_color;
String edited_font;
String edited_font_size;
Ref<Font> custom_font;
protected:
void _notification(int p_notification);
static void _bind_methods(){};
void _find_resources(EditorFileSystemDirectory *p_dir);
void _reload_fonts(const String &p_path);
void _update_fonts_menu();
void _update_styles_menu();
void _update_control();
void _font_selected(int p_id);
void _font_style_selected(int p_id);
void _set_font();
void _font_size_selected(double p_size);
void _outline_size_selected(double p_size);
void _font_color_changed(const Color &p_color);
void _outline_color_changed(const Color &p_color);
void _clear_formatting();
public:
void edit(Object *p_object);
bool handles(Object *p_object) const;
TextControlEditor();
};
/*************************************************************************/
class TextControlEditorPlugin : public EditorPlugin {
GDCLASS(TextControlEditorPlugin, EditorPlugin);
TextControlEditor *text_ctl_editor;
EditorNode *editor;
public:
virtual String get_name() const override { return "TextControlFontEditor"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
TextControlEditorPlugin(EditorNode *p_node);
};
#endif // TEXT_CONTROL_EDITOR_PLUGIN_H

View file

@ -65,18 +65,21 @@ void TextEditor::_load_theme_settings() {
String TextEditor::get_name() {
String name;
if (!text_file->is_built_in()) {
name = text_file->get_path().get_file();
if (is_unsaved()) {
if (text_file->get_path().is_empty()) {
name = TTR("[unsaved]");
}
name += "(*)";
name = text_file->get_path().get_file();
if (name.is_empty()) {
// This appears for newly created built-in text_files before saving the scene.
name = TTR("[unsaved]");
} else if (text_file->is_built_in()) {
const String &text_file_name = text_file->get_name();
if (text_file_name != "") {
// If the built-in text_file has a custom resource name defined,
// display the built-in text_file name as follows: `ResourceName (scene_file.tscn)`
name = vformat("%s (%s)", text_file_name, name.get_slice("::", 0));
}
} else if (text_file->get_name() != "") {
name = text_file->get_name();
} else {
name = text_file->get_class() + "(" + itos(text_file->get_instance_id()) + ")";
}
if (is_unsaved()) {
name += "(*)";
}
return name;

View file

@ -173,7 +173,7 @@ Texture3DEditor::Texture3DEditor() {
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.5));
info->add_theme_constant_override("shadow_as_outline", 1);
info->add_theme_constant_override("shadow_outline_size", 1);
info->add_theme_constant_override("shadow_offset_x", 2);
info->add_theme_constant_override("shadow_offset_y", 2);

View file

@ -101,7 +101,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
metadata_label->add_theme_color_override("font_outline_color", Color::named("black"));
metadata_label->add_theme_constant_override("outline_size", 2 * EDSCALE);
metadata_label->add_theme_constant_override("shadow_as_outline", 1);
metadata_label->add_theme_constant_override("shadow_outline_size", 1);
metadata_label->set_h_size_flags(Control::SIZE_SHRINK_END);
metadata_label->set_v_size_flags(Control::SIZE_SHRINK_END);

View file

@ -249,7 +249,7 @@ TextureLayeredEditor::TextureLayeredEditor() {
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.5));
info->add_theme_constant_override("shadow_as_outline", 1);
info->add_theme_constant_override("shadow_outline_size", 1);
info->add_theme_constant_override("shadow_offset_x", 2);
info->add_theme_constant_override("shadow_offset_y", 2);

View file

@ -47,8 +47,12 @@
TilesEditorPlugin *TilesEditorPlugin::singleton = nullptr;
void TilesEditorPlugin::_pattern_preview_done(const Variant &p_udata) {
pattern_preview_done.set();
void TilesEditorPlugin::_preview_frame_started() {
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_pattern_preview_done));
}
void TilesEditorPlugin::_pattern_preview_done() {
pattern_preview_done.post();
}
void TilesEditorPlugin::_thread_func(void *ud) {
@ -112,12 +116,9 @@ void TilesEditorPlugin::_thread() {
// Add the viewport at the lasst moment to avoid rendering too early.
EditorNode::get_singleton()->add_child(viewport);
pattern_preview_done.clear();
RS::get_singleton()->request_frame_drawn_callback(const_cast<TilesEditorPlugin *>(this), "_pattern_preview_done", Variant());
RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_preview_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
while (!pattern_preview_done.is_set()) {
OS::get_singleton()->delay_usec(10);
}
pattern_preview_done.wait();
Ref<Image> image = viewport->get_texture()->get_image();
Ref<ImageTexture> image_texture;
@ -274,10 +275,6 @@ bool TilesEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("TileMap") || p_object->is_class("TileSet");
}
void TilesEditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_pattern_preview_done", "pattern"), &TilesEditorPlugin::_pattern_preview_done);
}
TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
set_process_internal(true);

View file

@ -77,14 +77,14 @@ private:
Thread pattern_preview_thread;
SafeFlag pattern_thread_exit;
SafeFlag pattern_thread_exited;
mutable SafeFlag pattern_preview_done;
void _pattern_preview_done(const Variant &p_udata);
Semaphore pattern_preview_done;
void _preview_frame_started();
void _pattern_preview_done();
static void _thread_func(void *ud);
void _thread();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
_FORCE_INLINE_ static TilesEditorPlugin *get_singleton() { return singleton; }

View file

@ -4977,7 +4977,7 @@ public:
}
}
void setup(Ref<Resource> p_parent_resource, Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, Ref<VisualShaderNode> p_node) {
void setup(Ref<Resource> p_parent_resource, Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, const Map<StringName, String> &p_overrided_names, Ref<VisualShaderNode> p_node) {
parent_resource = p_parent_resource;
updating = false;
node = p_node;
@ -4993,7 +4993,11 @@ public:
Label *prop_name = memnew(Label);
String prop_name_str = p_names[i];
prop_name_str = prop_name_str.capitalize() + ":";
if (p_overrided_names.has(p_names[i])) {
prop_name_str = p_overrided_names[p_names[i]] + ":";
} else {
prop_name_str = prop_name_str.capitalize() + ":";
}
prop_name->set_text(prop_name_str);
prop_name->set_visible(false);
hbox->add_child(prop_name);
@ -5085,7 +5089,7 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<Resource> &p_par
properties.push_back(pinfo[i].name);
}
VisualShaderNodePluginDefaultEditor *editor = memnew(VisualShaderNodePluginDefaultEditor);
editor->setup(p_parent_resource, editors, properties, p_node);
editor->setup(p_parent_resource, editors, properties, p_node->get_editable_properties_names(), p_node);
return editor;
}

View file

@ -461,7 +461,7 @@ String RenameDialog::_substitute(const String &subject, const Node *node, int co
void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
RenameDialog *self = (RenameDialog *)p_self;
String source_file(p_file);
String source_file = String::utf8(p_file);
// Only show first error that is related to "regex"
if (self->has_errors || source_file.find("regex") < 0) {
@ -470,9 +470,9 @@ void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *
String err_str;
if (p_errorexp && p_errorexp[0]) {
err_str = p_errorexp;
err_str = String::utf8(p_errorexp);
} else {
err_str = p_error;
err_str = String::utf8(p_error);
}
self->has_errors = true;

View file

@ -47,6 +47,7 @@
#include "editor/plugins/script_editor_plugin.h"
#include "editor/shader_create_dialog.h"
#include "scene/main/window.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
#include "servers/display_server.h"
#include "servers/rendering_server.h"
@ -3132,7 +3133,9 @@ void SceneTreeDock::_clear_clipboard() {
void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap) {
List<PropertyInfo> props;
p_node->get_property_list(&props);
bool is_instantiated = EditorPropertyRevert::may_node_be_in_instance(p_node);
Vector<SceneState::PackState> states_stack;
bool states_stack_ready = false;
for (const PropertyInfo &E : props) {
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
@ -3143,13 +3146,14 @@ void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap)
if (v.is_ref()) {
RES res = v;
if (res.is_valid()) {
if (is_instantiated) {
Variant orig;
if (EditorPropertyRevert::get_instantiated_node_original_property(p_node, E.name, orig)) {
if (!EditorPropertyRevert::is_node_property_different(p_node, v, orig)) {
continue;
}
}
if (!states_stack_ready) {
states_stack = PropertyUtils::get_node_states_stack(p_node);
states_stack_ready = true;
}
Variant orig = PropertyUtils::get_property_default_value(p_node, E.name, &states_stack);
if (!PropertyUtils::is_property_value_different(v, orig)) {
continue;
}
if (res->is_built_in() && !r_remap.has(res)) {

View file

@ -2050,9 +2050,7 @@ bool Main::start() {
GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
GLOBAL_DEF("mono/profiler/enabled", false);
GLOBAL_DEF("mono/unhandled_exception_policy", 0);
// From editor/csharp_project.cpp.
GLOBAL_DEF("mono/project/auto_update_project", true);
GLOBAL_DEF("mono/runtime/unhandled_exception_policy", 0);
#endif
DocTools doc;
@ -2689,10 +2687,10 @@ bool Main::iteration() {
if (frame > 1000000) {
if (editor || project_manager) {
if (print_fps) {
print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(1)));
print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
} else if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) {
print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(1)));
print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
Engine::get_singleton()->_fps = frames;

Some files were not shown because too many files have changed in this diff Show more