diff --git a/core/global_constants.cpp b/core/global_constants.cpp index f3646bfe12e..ba86205d846 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -587,6 +587,7 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_ENUM_SUGGESTION); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LENGTH); + BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LINK); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_KEY_ACCEL); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_FLAGS); diff --git a/core/object.h b/core/object.h index cf9c8665548..56642b04e89 100644 --- a/core/object.h +++ b/core/object.h @@ -62,6 +62,7 @@ enum PropertyHint { PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout") PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer) + PROPERTY_HINT_LINK, PROPERTY_HINT_SPRITE_FRAME, // FIXME: Obsolete: drop whenever we can break compat. Keeping now for GDNative compat. PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer) PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags) diff --git a/core/os/os.cpp b/core/os/os.cpp index fcf712d442a..bc221dcb0c2 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -186,10 +186,6 @@ int OS::get_process_id() const { return -1; }; -void OS::vibrate_handheld(int p_duration_ms) { - WARN_PRINT("vibrate_handheld() only works with Android, iOS and HTML5"); -} - bool OS::is_stdout_verbose() const { return _verbose_stdout; } diff --git a/core/os/os.h b/core/os/os.h index 0e59d10ca33..39e3bcb51c5 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -365,7 +365,7 @@ public: virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual bool is_process_running(const ProcessID &p_pid) const = 0; - virtual void vibrate_handheld(int p_duration_ms = 500); + virtual void vibrate_handheld(int p_duration_ms = 500) {} virtual Error shell_open(String p_uri); virtual Error set_cwd(const String &p_cwd); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 9d7a6a2642b..4c40d59bcda 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1421,7 +1421,7 @@ Hints that an integer, float or string property is an enumerated value to pick in a list specified via a hint string. The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code]. - + Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code]. Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values. @@ -1431,63 +1431,66 @@ Deprecated hint, unused. - + + Hints that a vector property should allow linking values (e.g. to edit both [code]x[/code] and [code]y[/code] together). + + Deprecated hint, unused. - + Hints that an integer property is a bitmask with named bit flags. For example, to allow toggling bits 0, 1, 2 and 4, the hint could be something like [code]"Bit0,Bit1,Bit2,,Bit4"[/code]. - + Hints that an integer property is a bitmask using the optionally named 2D render layers. - + Hints that an integer property is a bitmask using the optionally named 2D physics layers. - + Hints that an integer property is a bitmask using the optionally named 2D navigation layers. - + Hints that an integer property is a bitmask using the optionally named 3D render layers. - + Hints that an integer property is a bitmask using the optionally named 3D physics layers. - + Hints that an integer property is a bitmask using the optionally named 3D navigation layers. - + Hints that a string property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. - + Hints that a string property is a path to a directory. Editing it will show a file dialog for picking the path. - + Hints that a string property is an absolute path to a file outside the project folder. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. - + Hints that a string property is an absolute path to a directory outside the project folder. Editing it will show a file dialog for picking the path. - + Hints that a property is an instance of a [Resource]-derived type, optionally specified via the hint string (e.g. [code]"Texture"[/code]). Editing it will show a popup menu of valid resource types to instantiate. - + Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed. - + Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use. - + Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited. - + Hints that an image is compressed using lossy compression. - + Hints that an image is compressed using lossless compression. - + - + Hint that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For instance: [codeblock] hint_string = "%s:" % [TYPE_INT] # Array of inteters. @@ -1497,34 +1500,34 @@ [/codeblock] [b]Note:[/b] The final colon is required to specify for properly detecting built-in types. - + - + - + - + - + - + - + - + - + - + - + - + - + Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country. - + The property is serialized and saved in the scene file (default). diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index f30d9c1c633..4f61556e430 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -364,7 +364,7 @@ - Starts to vibrate the joypad. Joypads usually come with two rumble motors, a strong and a weak one. [code]weak_magnitude[/code] is the strength of the weak motor (between 0 and 1) and [code]strong_magnitude[/code] is the strength of the strong motor (between 0 and 1). [code]duration[/code] is the duration of the effect in seconds (a duration of 0 will try to play the vibration indefinitely). + Starts to vibrate the joypad. Joypads usually come with two rumble motors, a strong and a weak one. [code]weak_magnitude[/code] is the strength of the weak motor (between 0 and 1) and [code]strong_magnitude[/code] is the strength of the strong motor (between 0 and 1). [code]duration[/code] is the duration of the effect in seconds (a duration of 0 will try to play the vibration indefinitely). The vibration can be stopped early by calling [method stop_joy_vibration]. [b]Note:[/b] Not every hardware is compatible with long effect durations; it is recommended to restart an effect if it has to be played for more than a few seconds. @@ -372,18 +372,18 @@ - Stops the vibration of the joypad. + Stops the vibration of the joypad started with [method start_joy_vibration]. - Vibrate handheld devices. - [b]Note:[/b] This method is implemented on Android, iOS, and HTML5. - [b]Note:[/b] For Android, it requires enabling the [code]VIBRATE[/code] permission in the export preset. - [b]Note:[/b] For iOS, specifying the duration is supported in iOS 13 and later. - [b]Note:[/b] Some web browsers such as Safari and Firefox for Android do not support this method. + Vibrate the handheld device for the specified duration in milliseconds. + [b]Note:[/b] This method is implemented on Android, iOS, and HTML5. It has no effect on other platforms. + [b]Note:[/b] For Android, [method vibrate_handheld] requires enabling the [code]VIBRATE[/code] permission in the export preset. Otherwise, [method vibrate_handheld] will have no effect. + [b]Note:[/b] For iOS, specifying the duration is only supported in iOS 13 and later. + [b]Note:[/b] Some web browsers such as Safari and Firefox for Android do not support [method vibrate_handheld]. diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml index bb30d3703fb..fe43c798af8 100644 --- a/doc/classes/LinkButton.xml +++ b/doc/classes/LinkButton.xml @@ -18,7 +18,16 @@ The button's text that will be displayed inside the button's area. - Determines when to show the underline. See [enum UnderlineMode] for options. + The underline mode to use for the text. See [enum LinkButton.UnderlineMode] for the available modes. + + + The [url=https://en.wikipedia.org/wiki/Uniform_Resource_Identifier]URI[/url] for this [LinkButton]. If set to a valid URI, pressing the button opens the URI using the operating system's default program for the protocol (via [method OS.shell_open]). HTTP and HTTPS URLs open the default web browser. + [b]Examples:[/b] + [codeblock] + uri = "https://godotengine.org" # Opens the URL in the default web browser. + uri = "C:\SomeFolder" # Opens the file explorer at the given path. + uri = "C:\SomeImage.png" # Opens the given image in the default viewing app. + [/codeblock] diff --git a/doc/classes/Navigation2DServer.xml b/doc/classes/Navigation2DServer.xml index cbb60ee7a22..2db55faf22d 100644 --- a/doc/classes/Navigation2DServer.xml +++ b/doc/classes/Navigation2DServer.xml @@ -40,12 +40,12 @@ - + - Callback called at the end of the RVO process. If a callback is created manually and the agent is placed on a navigation map it will calculate avoidance for the agent and dispatch the calculated [code]safe_velocity[/code] to the [code]receiver[/code] object with a signal to the chosen [code]method[/code] name. - [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]null[/code] object as the [code]receiver[/code]. + Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations. + [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code]. diff --git a/doc/classes/NavigationServer.xml b/doc/classes/NavigationServer.xml index 663663898e9..9252e4566b0 100644 --- a/doc/classes/NavigationServer.xml +++ b/doc/classes/NavigationServer.xml @@ -40,12 +40,12 @@ - + - Callback called at the end of the RVO process. If a callback is created manually and the agent is placed on a navigation map it will calculate avoidance for the agent and dispatch the calculated [code]safe_velocity[/code] to the [code]receiver[/code] object with a signal to the chosen [code]method[/code] name. - [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]null[/code] object as the [code]receiver[/code]. + Sets the callback [code]object_id[/code] and [code]method[/code] that gets called after each avoidance processing step for the [code]agent[/code]. The calculated [code]safe_velocity[/code] will be dispatched with a signal to the object just before the physics calculations. + [b]Note:[/b] Created callbacks are always processed independently of the SceneTree state as long as the agent is on a navigation map and not freed. To disable the dispatch of a callback from an agent use [method agent_set_callback] again with a [code]0[/code] ObjectID as the [code]object_id[/code]. diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 78393afcbcb..8e18bb35f1c 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1509,10 +1509,22 @@ void EditorPropertyVector2::_value_changed(double val, const String &p_name) { return; } + if (linked->is_pressed()) { + setting = true; + if (p_name == "x") { + spin[1]->set_value(spin[0]->get_value() * ratio_yx); + } + + if (p_name == "y") { + spin[0]->set_value(spin[1]->get_value() * ratio_xy); + } + setting = false; + } + Vector2 v2; v2.x = spin[0]->get_value(); v2.y = spin[1]->get_value(); - emit_changed(get_edited_property(), v2, p_name); + emit_changed(get_edited_property(), v2, linked->is_pressed() ? "" : p_name); } void EditorPropertyVector2::update_property() { @@ -1521,24 +1533,46 @@ void EditorPropertyVector2::update_property() { spin[0]->set_value(val.x); spin[1]->set_value(val.y); setting = false; + _update_ratio(); +} + +void EditorPropertyVector2::_update_ratio() { + linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5)); + + if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) { + ratio_xy = spin[0]->get_value() / spin[1]->get_value(); + ratio_yx = spin[1]->get_value() / spin[0]->get_value(); + } else { + ratio_xy = 1.0; + ratio_yx = 1.0; + } } void EditorPropertyVector2::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - Color base = get_color("accent_color", "Editor"); - for (int i = 0; i < 2; i++) { - Color c = base; - c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); - spin[i]->set_custom_label_color(true, c); - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + Ref normal_icon = get_icon("Unlinked", "EditorIcons"); + linked->set_custom_minimum_size(Vector2(normal_icon->get_width(), 0)); + linked->set_normal_texture(normal_icon); + linked->set_pressed_texture(get_icon("Instance", "EditorIcons")); + + Color base = get_color("accent_color", "Editor"); + for (int i = 0; i < 2; i++) { + Color c = base; + c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); + spin[i]->set_custom_label_color(true, c); + } + } break; } } void EditorPropertyVector2::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_ratio"), &EditorPropertyVector2::_update_ratio); ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector2::_value_changed); } -void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider) { +void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_link) { for (int i = 0; i < 2; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); @@ -1547,21 +1581,31 @@ void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, boo spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); } + + if (!p_link) { + linked->hide(); + } else { + linked->set_pressed(true); + } } EditorPropertyVector2::EditorPropertyVector2() { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector2_editing"); + HBoxContainer *hb = memnew(HBoxContainer); + hb->set_h_size_flags(SIZE_EXPAND_FILL); + BoxContainer *bc; if (horizontal) { bc = memnew(HBoxContainer); - add_child(bc); - set_bottom_editor(bc); + hb->add_child(bc); + set_bottom_editor(hb); } else { bc = memnew(VBoxContainer); - add_child(bc); + hb->add_child(bc); } + bc->set_h_size_flags(SIZE_EXPAND_FILL); static const char *desc[2] = { "x", "y" }; for (int i = 0; i < 2; i++) { @@ -1576,6 +1620,14 @@ EditorPropertyVector2::EditorPropertyVector2() { } } + linked = memnew(TextureButton); + linked->set_toggle_mode(true); + linked->set_expand(true); + linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED); + linked->connect("pressed", this, "_update_ratio"); + hb->add_child(linked); + + add_child(hb); if (!horizontal) { set_label_reference(spin[0]); //show text and buttons around this } @@ -1671,11 +1723,30 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) { return; } + if (linked->is_pressed()) { + setting = true; + if (p_name == "x") { + spin[1]->set_value(spin[0]->get_value() * ratio_yx); + spin[2]->set_value(spin[0]->get_value() * ratio_zx); + } + + if (p_name == "y") { + spin[0]->set_value(spin[1]->get_value() * ratio_xy); + spin[2]->set_value(spin[1]->get_value() * ratio_zy); + } + + if (p_name == "z") { + spin[0]->set_value(spin[2]->get_value() * ratio_xz); + spin[1]->set_value(spin[2]->get_value() * ratio_yz); + } + setting = false; + } + Vector3 v3; v3.x = spin[0]->get_value(); v3.y = spin[1]->get_value(); v3.z = spin[2]->get_value(); - emit_changed(get_edited_property(), v3, p_name); + emit_changed(get_edited_property(), v3, linked->is_pressed() ? "" : p_name); } void EditorPropertyVector3::update_property() { @@ -1685,22 +1756,54 @@ void EditorPropertyVector3::update_property() { spin[1]->set_value(val.y); spin[2]->set_value(val.z); setting = false; + + _update_ratio(); } + +void EditorPropertyVector3::_update_ratio() { + linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5)); + + if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) { + ratio_yx = spin[1]->get_value() / spin[0]->get_value(); + ratio_zx = spin[2]->get_value() / spin[0]->get_value(); + ratio_xy = spin[0]->get_value() / spin[1]->get_value(); + ratio_zy = spin[2]->get_value() / spin[1]->get_value(); + ratio_xz = spin[0]->get_value() / spin[2]->get_value(); + ratio_yz = spin[1]->get_value() / spin[2]->get_value(); + } else { + ratio_yx = 1.0; + ratio_zx = 1.0; + ratio_xy = 1.0; + ratio_zy = 1.0; + ratio_xz = 1.0; + ratio_yz = 1.0; + } +} + void EditorPropertyVector3::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - Color base = get_color("accent_color", "Editor"); - for (int i = 0; i < 3; i++) { - Color c = base; - c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); - spin[i]->set_custom_label_color(true, c); - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + Ref normal_icon = get_icon("Unlinked", "EditorIcons"); + linked->set_custom_minimum_size(Vector2(normal_icon->get_width(), 0)); + linked->set_normal_texture(normal_icon); + linked->set_pressed_texture(get_icon("Instance", "EditorIcons")); + + Color base = get_color("accent_color", "Editor"); + for (int i = 0; i < 3; i++) { + Color c = base; + c.set_hsv(float(i) / 3.0 + 0.05, c.get_s() * 0.75, c.get_v()); + spin[i]->set_custom_label_color(true, c); + } + } break; } } void EditorPropertyVector3::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_ratio"), &EditorPropertyVector3::_update_ratio); ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector3::_value_changed); } -void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider) { +void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_link) { for (int i = 0; i < 3; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); @@ -1709,21 +1812,31 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo spin[i]->set_allow_greater(true); spin[i]->set_allow_lesser(true); } + + if (!p_link) { + linked->hide(); + } else { + linked->set_pressed(true); + } } EditorPropertyVector3::EditorPropertyVector3() { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + HBoxContainer *hb = memnew(HBoxContainer); + hb->set_h_size_flags(SIZE_EXPAND_FILL); + BoxContainer *bc; if (horizontal) { bc = memnew(HBoxContainer); - add_child(bc); - set_bottom_editor(bc); + hb->add_child(bc); + set_bottom_editor(hb); } else { bc = memnew(VBoxContainer); - add_child(bc); + hb->add_child(bc); } + bc->set_h_size_flags(SIZE_EXPAND_FILL); static const char *desc[3] = { "x", "y", "z" }; for (int i = 0; i < 3; i++) { @@ -1738,11 +1851,21 @@ EditorPropertyVector3::EditorPropertyVector3() { } } + linked = memnew(TextureButton); + linked->set_toggle_mode(true); + linked->set_expand(true); + linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED); + linked->connect("pressed", this, "_update_ratio"); + hb->add_child(linked); + + add_child(hb); if (!horizontal) { set_label_reference(spin[0]); //show text and buttons around this } setting = false; + _update_ratio(); } + ///////////////////// PLANE ///////////////////////// void EditorPropertyPlane::_value_changed(double val, const String &p_name) { @@ -2485,7 +2608,10 @@ void EditorPropertyResource::_resource_selected(const RES &p_resource, bool p_ed void EditorPropertyResource::_resource_changed(const RES &p_resource) { // Make visual script the correct type. Ref