diff options
36 files changed, 331 insertions, 183 deletions
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 3b2e260dcb..55810156a1 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2654,7 +2654,7 @@ </constant> <constant name="PROPERTY_HINT_ENUM" value="2" enum="PropertyHint"> Hints that an [int] 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]. + 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 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]. </constant> <constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="3" enum="PropertyHint"> 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]. @@ -2667,7 +2667,10 @@ Hints that a vector property should allow its components to be linked. For example, this allows [member Vector2.x] and [member Vector2.y] to be edited together. </constant> <constant name="PROPERTY_HINT_FLAGS" value="6" enum="PropertyHint"> - Hints that an [int] 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 [int] property is a bitmask with named bit flags. + The hint string is a comma separated list of names such as [code]"Bit0,Bit1,Bit2,Bit3"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. The first name in the list has value 1, the next 2, then 4, 8, 16 and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"A:4,B:8,C:16"[/code]. You can also combine several flags ([code]"A:4,B:8,AB:12,C:16"[/code]). + [b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code]. + [b]Note:[/b] Unlike [constant PROPERTY_HINT_ENUM], the previous explicit value is not taken into account. For the hint [code]"A:16,B,C"[/code], A is 16, B is 2, C is 4. </constant> <constant name="PROPERTY_HINT_LAYERS_2D_RENDER" value="7" enum="PropertyHint"> Hints that an [int] property is a bitmask using the optionally named 2D render layers. diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 020ce4f468..29011cd016 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -138,7 +138,7 @@ Used with [method Node.rpc_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods. </constant> <constant name="RPC_MODE_ANY_PEER" value="1" enum="RPCMode"> - Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc("any")[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. + Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc("any_peer")[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not. </constant> <constant name="RPC_MODE_AUTHORITY" value="2" enum="RPCMode"> Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc("authority")[/code] annotation. See [method Node.set_multiplayer_authority]. diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 54e14074d9..2d3ec437c6 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -367,7 +367,6 @@ void EditorSpinSlider::_draw_spin_slider() { if (!hide_slider) { if (get_step() == 1) { - number_width -= updown->get_width(); Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox")); int updown_vofs = (size.height - updown2->get_height()) / 2; if (rtl) { diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index e05b17168d..923b2fe30d 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -304,7 +304,7 @@ <return type="void" /> <param index="0" name="names" type="String" /> <description> - Export an [int] or [String] property as an enumerated list of options. If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon. If the property is a [String], then the value is stored. + Export an [int] or [String] property as an enumerated list of options. If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add explicit values using a colon. If the property is a [String], then the value is stored. See also [constant PROPERTY_HINT_ENUM]. [codeblock] @export_enum("Warrior", "Magician", "Thief") var character_class: int @@ -357,6 +357,20 @@ [codeblock] @export_flags("Fire", "Water", "Earth", "Wind") var spell_elements = 0 [/codeblock] + You can add explicit values using a colon: + [codeblock] + @export_flags("Self:4", "Allies:8", "Foes:16") var spell_targets = 0 + [/codeblock] + You can also combine several flags: + [codeblock] + @export_flags("Self:4", "Allies:8", "Self and Allies:12", "Foes:16") + var spell_targets = 0 + [/codeblock] + [b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code]. + [b]Note:[/b] Unlike [annotation @export_enum], the previous explicit value is not taken into account. In the following example, A is 16, B is 2, C is 4. + [codeblock] + @export_flags("A:16", "B", "C") var x + [/codeblock] </description> </annotation> <annotation name="@export_flags_2d_navigation"> diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 78e437b42a..602d07d9a7 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1576,11 +1576,8 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun resolve_suite(p_function->body); - GDScriptParser::DataType return_type = p_function->body->get_datatype(); - - if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) { + if (!p_function->get_datatype().is_hard_type() && p_function->body->get_datatype().is_set()) { // Use the suite inferred type if return isn't explicitly set. - return_type.type_source = GDScriptParser::DataType::INFERRED; p_function->set_datatype(p_function->body->get_datatype()); } else if (p_function->get_datatype().is_hard_type() && (p_function->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_function->get_datatype().builtin_type != Variant::NIL)) { if (!p_function->body->has_return && (p_is_lambda || p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init)) { @@ -3017,7 +3014,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else if (!is_self && base_type.is_meta_type && !is_static) { base_type.is_meta_type = false; // For `to_string()`. - push_error(vformat(R"*(Cannot call non-static function "%s()" on a class directly. Make an instance instead.)*", p_call->function_name), p_call); + push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); } else if (is_self && !is_static) { mark_lambda_use_self(); } @@ -4564,6 +4561,16 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo base_script = base_script->get_base_script(); } + // If the base is a script, it might be trying to access members of the Script class itself. + if (p_base_type.is_meta_type && !p_is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) { + MethodInfo info; + StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static()); + + if (ClassDB::get_method_info(script_class, function_name, &info)) { + return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); + } + } + if (p_is_constructor) { // Native types always have a default constructor. r_return_type = p_base_type; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 210550a674..46cd4b0d55 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -591,6 +591,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } else if (callee->type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); + if (subscript->is_attribute) { // May be static built-in method call. if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { @@ -614,7 +615,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else { class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type; } - if (!subscript->base->is_constant && ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { + if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { MethodBind *method = ClassDB::get_method(class_name, call->function_name); if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 8cfd48b52b..3543c0a79f 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -3491,6 +3491,14 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co break; } + if (context.current_class) { + if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) { + base.type = context.current_class->get_datatype(); + } else { + base.type = context.current_class->base_type; + } + } + if (_lookup_symbol_from_base(base.type, p_symbol, is_function, r_result) == OK) { return OK; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0a1a64cb59..c6e4222213 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3637,15 +3637,6 @@ template <PropertyHint t_hint, Variant::Type t_type> bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) { ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name)); - { - const int max_flags = 32; - - if (t_hint == PropertyHint::PROPERTY_HINT_FLAGS && p_annotation->resolved_arguments.size() > max_flags) { - push_error(vformat(R"(The argument count limit for "@export_flags" is exceeded (%d/%d).)", p_annotation->resolved_arguments.size(), max_flags), p_annotation); - return false; - } - } - VariableNode *variable = static_cast<VariableNode *>(p_node); if (variable->exported) { push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation); @@ -3659,14 +3650,50 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node String hint_string; for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) { - if (p_annotation->name != SNAME("@export_placeholder") && String(p_annotation->resolved_arguments[i]).contains(",")) { - push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]); - return false; + String arg_string = String(p_annotation->resolved_arguments[i]); + + if (p_annotation->name != SNAME("@export_placeholder")) { + if (arg_string.is_empty()) { + push_error(vformat(R"(Argument %d of annotation "%s" is empty.)", i + 1, p_annotation->name), p_annotation->arguments[i]); + return false; + } + if (arg_string.contains(",")) { + push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]); + return false; + } + } + + if (p_annotation->name == SNAME("@export_flags")) { + const int64_t max_flags = 32; + Vector<String> t = arg_string.split(":", true, 1); + if (t[0].is_empty()) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Expected flag name.)", i + 1), p_annotation->arguments[i]); + return false; + } + if (t.size() == 2) { + if (t[1].is_empty()) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Expected flag value.)", i + 1), p_annotation->arguments[i]); + return false; + } + if (!t[1].is_valid_int()) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": The flag value must be a valid integer.)", i + 1), p_annotation->arguments[i]); + return false; + } + int64_t value = t[1].to_int(); + if (value < 1 || value >= (1LL << max_flags)) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": The flag value must be at least 1 and at most 2 ** %d - 1.)", i + 1, max_flags), p_annotation->arguments[i]); + return false; + } + } else if (i >= max_flags) { + push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Starting from argument %d, the flag value must be specified explicitly.)", i + 1, max_flags + 1), p_annotation->arguments[i]); + return false; + } } + if (i > 0) { hint_string += ","; } - hint_string += String(p_annotation->resolved_arguments[i]); + hint_string += arg_string; } variable->export_info.hint_string = hint_string; diff --git a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.gd b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.gd deleted file mode 100644 index 966d2b0aa2..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.gd +++ /dev/null @@ -1,5 +0,0 @@ -const TestClass = preload("gdscript_duplicate_class.notest.gd") - -func test(): - # (TestClass as GDScript).duplicate() exists - TestClass.duplicate() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.out b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.out deleted file mode 100644 index b2c7fec86e..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Cannot call non-static function "duplicate()" on a class directly. Make an instance instead. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate_class.notest.gd b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate_class.notest.gd deleted file mode 100644 index 61510e14cd..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate_class.notest.gd +++ /dev/null @@ -1 +0,0 @@ -extends Node diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.gd deleted file mode 100644 index 030daf502c..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.gd +++ /dev/null @@ -1,6 +0,0 @@ -const TestClass = preload("gdscript_duplicate_class.notest.gd") - -func test(): - # TestClass.duplicate() fails - @warning_ignore("return_value_discarded") - (TestClass as GDScript).duplicate() diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.out b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.out deleted file mode 100644 index d73c5eb7cd..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.out +++ /dev/null @@ -1 +0,0 @@ -GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate_class.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate_class.notest.gd deleted file mode 100644 index 61510e14cd..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate_class.notest.gd +++ /dev/null @@ -1 +0,0 @@ -extends Node diff --git a/modules/gdscript/tests/scripts/parser/features/super_class_check.gd b/modules/gdscript/tests/scripts/parser/features/super_class_check.gd deleted file mode 100644 index edfc45a8d8..0000000000 --- a/modules/gdscript/tests/scripts/parser/features/super_class_check.gd +++ /dev/null @@ -1,13 +0,0 @@ -# https://github.com/godotengine/godot/issues/71994 - -func test(): - pass - -class A extends RefCounted: - pass - -class B extends A: - # Parsing `duplicate()` here would throw this error: - # Parse Error: The function signature doesn't match the parent. Parent signature is "duplicate(bool = default) -> Resource". - func duplicate(): - pass diff --git a/modules/gdscript/tests/scripts/parser/features/super_class_check.out b/modules/gdscript/tests/scripts/parser/features/super_class_check.out deleted file mode 100644 index d73c5eb7cd..0000000000 --- a/modules/gdscript/tests/scripts/parser/features/super_class_check.out +++ /dev/null @@ -1 +0,0 @@ -GDTEST_OK diff --git a/modules/gdscript/tests/scripts/runtime/features/static_duplicate.gd b/modules/gdscript/tests/scripts/runtime/features/static_duplicate.gd deleted file mode 100644 index 418501dcc5..0000000000 --- a/modules/gdscript/tests/scripts/runtime/features/static_duplicate.gd +++ /dev/null @@ -1,19 +0,0 @@ -const PreloadClass = preload("static_duplicate_preload.notest.gd") -const PreloadClassAlias = PreloadClass - -func test(): - var dup_preload_one = PreloadClass.duplicate() - print(dup_preload_one == Vector2.ONE) - - var dup_preload_two = (PreloadClass as GDScript).duplicate() - print(dup_preload_two is GDScript) - - var dup_preload_alias_one = PreloadClassAlias.duplicate() - print(dup_preload_alias_one == Vector2.ONE) - - var dup_preload_alias_two = (PreloadClassAlias as GDScript).duplicate() - print(dup_preload_alias_two is GDScript) - - var PreloadClassAsGDScript = PreloadClass as GDScript - var dup_preload_class_as_gdscript_one = PreloadClassAsGDScript.duplicate() - print(dup_preload_class_as_gdscript_one is GDScript) diff --git a/modules/gdscript/tests/scripts/runtime/features/static_duplicate.out b/modules/gdscript/tests/scripts/runtime/features/static_duplicate.out deleted file mode 100644 index 34cd5c7652..0000000000 --- a/modules/gdscript/tests/scripts/runtime/features/static_duplicate.out +++ /dev/null @@ -1,9 +0,0 @@ -GDTEST_OK -preload duplicate -true -true -preload duplicate -true -true -preload duplicate -false diff --git a/modules/gdscript/tests/scripts/runtime/features/static_duplicate_preload.notest.gd b/modules/gdscript/tests/scripts/runtime/features/static_duplicate_preload.notest.gd deleted file mode 100644 index 291ffc2c0b..0000000000 --- a/modules/gdscript/tests/scripts/runtime/features/static_duplicate_preload.notest.gd +++ /dev/null @@ -1,5 +0,0 @@ -extends RefCounted - -static func duplicate() -> Vector2: - print("preload duplicate") - return Vector2.ONE diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs index a4d5e1a569..1e4fd2f09a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs @@ -22,8 +22,7 @@ namespace GodotTools.Build // TODO Use List once we have proper serialization public Godot.Collections.Array CustomProperties { get; private set; } = new(); - public string LogsDirPath => - Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.Md5Text()}_{Configuration}"); + public string LogsDirPath => GodotSharpDirs.LogsDirPathFor(Solution, Configuration); public override bool Equals(object? obj) { diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs index ae0ffaf4cb..d6549c1b70 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Godot; using GodotTools.BuildLogger; using GodotTools.Utils; @@ -22,9 +23,11 @@ namespace GodotTools.Build if (dotnetPath == null) throw new FileNotFoundException("Cannot find the dotnet executable."); + var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var startInfo = new ProcessStartInfo(dotnetPath); - BuildArguments(buildInfo, startInfo.ArgumentList); + BuildArguments(buildInfo, startInfo.ArgumentList, editorSettings); string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString(); stdOutHandler?.Invoke(launchMessage); @@ -35,6 +38,8 @@ namespace GodotTools.Build startInfo.RedirectStandardError = true; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; + startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] + = ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-'); // Needed when running from Developer Command Prompt for VS RemovePlatformVariable(startInfo.EnvironmentVariables); @@ -83,9 +88,11 @@ namespace GodotTools.Build if (dotnetPath == null) throw new FileNotFoundException("Cannot find the dotnet executable."); + var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var startInfo = new ProcessStartInfo(dotnetPath); - BuildPublishArguments(buildInfo, startInfo.ArgumentList); + BuildPublishArguments(buildInfo, startInfo.ArgumentList, editorSettings); string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString(); stdOutHandler?.Invoke(launchMessage); @@ -95,6 +102,8 @@ namespace GodotTools.Build startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; startInfo.UseShellExecute = false; + startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] + = ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-'); // Needed when running from Developer Command Prompt for VS RemovePlatformVariable(startInfo.EnvironmentVariables); @@ -124,7 +133,8 @@ namespace GodotTools.Build } } - private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments) + private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments, + EditorSettings editorSettings) { // `dotnet clean` / `dotnet build` commands arguments.Add(buildInfo.OnlyClean ? "clean" : "build"); @@ -150,12 +160,14 @@ namespace GodotTools.Build arguments.Add(buildInfo.Configuration); // Verbosity - arguments.Add("-v"); - arguments.Add("normal"); + AddVerbosityArguments(buildInfo, arguments, editorSettings); // Logger AddLoggerArgument(buildInfo, arguments); + // Binary log + AddBinaryLogArgument(buildInfo, arguments, editorSettings); + // Custom properties foreach (var customProperty in buildInfo.CustomProperties) { @@ -163,7 +175,8 @@ namespace GodotTools.Build } } - private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments) + private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments, + EditorSettings editorSettings) { arguments.Add("publish"); // `dotnet publish` command @@ -193,12 +206,14 @@ namespace GodotTools.Build arguments.Add("true"); // Verbosity - arguments.Add("-v"); - arguments.Add("normal"); + AddVerbosityArguments(buildInfo, arguments, editorSettings); // Logger AddLoggerArgument(buildInfo, arguments); + // Binary log + AddBinaryLogArgument(buildInfo, arguments, editorSettings); + // Custom properties foreach (var customProperty in buildInfo.CustomProperties) { @@ -213,6 +228,25 @@ namespace GodotTools.Build } } + private static void AddVerbosityArguments(BuildInfo buildInfo, Collection<string> arguments, + EditorSettings editorSettings) + { + var verbosityLevel = + editorSettings.GetSetting(GodotSharpEditor.Settings.VerbosityLevel).As<VerbosityLevelId>(); + arguments.Add("-v"); + arguments.Add(verbosityLevel switch + { + VerbosityLevelId.Quiet => "quiet", + VerbosityLevelId.Minimal => "minimal", + VerbosityLevelId.Detailed => "detailed", + VerbosityLevelId.Diagnostic => "diagnostic", + _ => "normal", + }); + + if ((bool)editorSettings.GetSetting(GodotSharpEditor.Settings.NoConsoleLogging)) + arguments.Add("-noconlog"); + } + private static void AddLoggerArgument(BuildInfo buildInfo, Collection<string> arguments) { string buildLoggerPath = Path.Combine(Internals.GodotSharpDirs.DataEditorToolsDir, @@ -222,6 +256,16 @@ namespace GodotTools.Build $"-l:{typeof(GodotBuildLogger).FullName},{buildLoggerPath};{buildInfo.LogsDirPath}"); } + private static void AddBinaryLogArgument(BuildInfo buildInfo, Collection<string> arguments, + EditorSettings editorSettings) + { + if (!(bool)editorSettings.GetSetting(GodotSharpEditor.Settings.CreateBinaryLog)) + return; + + arguments.Add($"-bl:{Path.Combine(buildInfo.LogsDirPath, "msbuild.binlog")}"); + arguments.Add("-ds:False"); // Honestly never understood why -bl also switches -ds on. + } + private static void RemovePlatformVariable(StringDictionary environmentVariables) { // EnvironmentVariables is case sensitive? Seriously? diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 262de024ca..cf1b84e37f 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Godot; using GodotTools.Internals; using static GodotTools.Internals.Globals; @@ -14,6 +15,7 @@ namespace GodotTools.Build private Button _errorsBtn; private Button _warningsBtn; private Button _viewLogBtn; + private Button _openLogsFolderBtn; private void WarningsToggled(bool pressed) { @@ -93,6 +95,10 @@ namespace GodotTools.Build private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed; + private void OpenLogsFolderPressed() => OS.ShellOpen( + $"file://{GodotSharpDirs.LogsDirPathFor("Debug")}" + ); + private void BuildMenuOptionPressed(long id) { switch ((BuildMenuOptions)id) @@ -171,6 +177,22 @@ namespace GodotTools.Build _viewLogBtn.Toggled += ViewLogToggled; toolBarHBox.AddChild(_viewLogBtn); + // Horizontal spacer, push everything to the right. + toolBarHBox.AddChild(new Control + { + SizeFlagsHorizontal = SizeFlags.ExpandFill, + }); + + _openLogsFolderBtn = new Button + { + Text = "Show Logs in File Manager".TTR(), + Icon = GetThemeIcon("Filesystem", "EditorIcons"), + ExpandIcon = false, + FocusMode = FocusModeEnum.None, + }; + _openLogsFolderBtn.Pressed += OpenLogsFolderPressed; + toolBarHBox.AddChild(_openLogsFolderBtn); + BuildOutputView = new BuildOutputView(); AddChild(BuildOutputView); } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 5abbe8752c..43ead4af69 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -22,6 +22,14 @@ namespace GodotTools { public partial class GodotSharpEditor : EditorPlugin, ISerializationListener { + public static class Settings + { + public const string ExternalEditor = "dotnet/editor/external_editor"; + public const string VerbosityLevel = "dotnet/build/verbosity_level"; + public const string NoConsoleLogging = "dotnet/build/no_console_logging"; + public const string CreateBinaryLog = "dotnet/build/create_binary_log"; + } + private EditorSettings _editorSettings; private PopupMenu _menuPopup; @@ -171,7 +179,7 @@ namespace GodotTools [UsedImplicitly] public Error OpenInExternalEditor(Script script, int line, int col) { - var editorId = (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor"); + var editorId = _editorSettings.GetSetting(Settings.ExternalEditor).As<ExternalEditorId>(); switch (editorId) { @@ -323,8 +331,7 @@ namespace GodotTools [UsedImplicitly] public bool OverridesExternalEditor() { - return (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor") != - ExternalEditorId.None; + return _editorSettings.GetSetting(Settings.ExternalEditor).As<ExternalEditorId>() != ExternalEditorId.None; } public override bool _Build() @@ -453,7 +460,10 @@ namespace GodotTools _menuPopup.IdPressed += _MenuOptionPressed; // External editor settings - EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None)); + EditorDef(Settings.ExternalEditor, Variant.From(ExternalEditorId.None)); + EditorDef(Settings.VerbosityLevel, Variant.From(VerbosityLevelId.Normal)); + EditorDef(Settings.NoConsoleLogging, false); + EditorDef(Settings.CreateBinaryLog, false); string settingsHintStr = "Disabled"; @@ -481,11 +491,23 @@ namespace GodotTools _editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary { ["type"] = (int)Variant.Type.Int, - ["name"] = "mono/editor/external_editor", + ["name"] = Settings.ExternalEditor, ["hint"] = (int)PropertyHint.Enum, ["hint_string"] = settingsHintStr }); + var verbosityLevels = Enum.GetValues<VerbosityLevelId>().Select(level => $"{Enum.GetName(level)}:{(int)level}"); + _editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary + { + ["type"] = (int)Variant.Type.Int, + ["name"] = Settings.VerbosityLevel, + ["hint"] = (int)PropertyHint.Enum, + ["hint_string"] = string.Join(",", verbosityLevels), + }); + + OnSettingsChanged(); + _editorSettings.SettingsChanged += OnSettingsChanged; + // Export plugin var exportPlugin = new ExportPlugin(); AddExportPlugin(exportPlugin); @@ -510,6 +532,24 @@ namespace GodotTools AddChild(GodotIdeManager); } + public override void _DisablePlugin() + { + base._DisablePlugin(); + + _editorSettings.SettingsChanged -= OnSettingsChanged; + } + + private void OnSettingsChanged() + { + // We want to force NoConsoleLogging to true when the VerbosityLevel is at Detailed or above. + // At that point, there's so much info logged that it doesn't make sense to display it in + // the tiny editor window, and it'd make the editor hang or crash anyway. + var verbosityLevel = _editorSettings.GetSetting(Settings.VerbosityLevel).As<VerbosityLevelId>(); + var hideConsoleLog = (bool)_editorSettings.GetSetting(Settings.NoConsoleLogging); + if (verbosityLevel >= VerbosityLevelId.Detailed && !hideConsoleLog) + _editorSettings.SetSetting(Settings.NoConsoleLogging, Variant.From(true)); + } + protected override void Dispose(bool disposing) { if (disposing) diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 5de2c9833b..83621ce5af 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -21,7 +21,8 @@ namespace GodotTools.Ides return _messagingServer; _messagingServer?.Dispose(); - _messagingServer = new MessagingServer(OS.GetExecutablePath(), ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger()); + _messagingServer = new MessagingServer(OS.GetExecutablePath(), + ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger()); _ = _messagingServer.Listen(); @@ -76,8 +77,8 @@ namespace GodotTools.Ides public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000) { - var editorId = (ExternalEditorId)(int)GodotSharpEditor.Instance.GetEditorInterface() - .GetEditorSettings().GetSetting("mono/editor/external_editor"); + var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); + var editorId = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>(); string editorIdentity = GetExternalEditorIdentity(editorId); var runningServer = GetRunningOrNewServer(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs index 60602a5847..f55ca4c7d7 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs @@ -9,7 +9,7 @@ namespace GodotTools.Ides.Rider { public static class RiderPathManager { - public static readonly string EditorPathSettingName = "mono/editor/editor_path_optional"; + public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional"; private static string GetRiderPathFromSettings() { @@ -22,7 +22,7 @@ namespace GodotTools.Ides.Rider public static void Initialize() { var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); - var editor = (ExternalEditorId)(int)editorSettings.GetSetting("mono/editor/external_editor"); + var editor = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>(); if (editor == ExternalEditorId.Rider) { if (!editorSettings.HasSetting(EditorPathSettingName)) diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs index 7624989092..fb68fcbae6 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -115,5 +115,11 @@ namespace GodotTools.Internals return _projectCsProjPath; } } + + public static string LogsDirPathFor(string solution, string configuration) + => Path.Combine(BuildLogsDirs, $"{solution.Md5Text()}_{configuration}"); + + public static string LogsDirPathFor(string configuration) + => LogsDirPathFor(ProjectSlnPath, configuration); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs b/modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs new file mode 100644 index 0000000000..0e1afe6bbf --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs @@ -0,0 +1,11 @@ +namespace GodotTools +{ + public enum VerbosityLevelId : long + { + Quiet, + Minimal, + Normal, + Detailed, + Diagnostic, + } +} diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 436b936586..77bf15125e 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -162,12 +162,10 @@ void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_p rev_pos.y = 0; rev_pos.normalize(); - if (channel_count >= 1) { - // Stereo pair - float c = rev_pos.x * 0.5 + 0.5; - reverb_vol.write[0].l = 1.0 - c; - reverb_vol.write[0].r = c; - } + // Stereo pair. + float c = rev_pos.x * 0.5 + 0.5; + reverb_vol.write[0].l = 1.0 - c; + reverb_vol.write[0].r = c; if (channel_count >= 3) { // Center pair + Side pair @@ -183,7 +181,6 @@ void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_p if (channel_count >= 4) { // Rear pair // FIXME: Not sure what math should be done here - float c = rev_pos.x * 0.5 + 0.5; reverb_vol.write[3].l = 1.0 - c; reverb_vol.write[3].r = c; } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 8087ac6250..ad0c19b1e6 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1233,7 +1233,7 @@ void AnimationPlayer::_animation_update_transforms() { if (aa->time < pasi.start) { stop = true; } - } else if (aa->backward) { + } else { if (aa->time > pasi.start) { stop = true; } diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index dfe9ea3b08..760c86b2cc 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -276,10 +276,6 @@ Size2 AcceptDialog::_get_contents_minimum_size() const { // Plus there is a separation size added on top. content_minsize.y += theme_cache.buttons_separation; - // Last, we make sure that we aren't below the minimum window size. - Size2 window_minsize = get_min_size(); - content_minsize.x = MAX(window_minsize.x, content_minsize.x); - content_minsize.y = MAX(window_minsize.y, content_minsize.y); return content_minsize; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index cbb34a480b..a1e7555a5d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -380,7 +380,7 @@ void Viewport::_notification(int p_what) { if (get_tree()->is_debugging_collisions_hint()) { PhysicsServer2D::get_singleton()->space_set_debug_contacts(find_world_2d()->get_space(), get_tree()->get_collision_debug_contact_count()); contact_2d_debug = RenderingServer::get_singleton()->canvas_item_create(); - RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, find_world_2d()->get_canvas()); + RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, current_canvas); #ifndef _3D_DISABLED PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count()); contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create(); @@ -1172,11 +1172,16 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { } Vector2 Viewport::get_mouse_position() const { - return gui.last_mouse_pos; + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) { + return get_screen_transform_internal(true).affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position()); + } else { + // Fallback to Input for getting mouse position in case of emulated mouse. + return get_screen_transform_internal().affine_inverse().xform(Input::get_singleton()->get_mouse_position()); + } } void Viewport::warp_mouse(const Vector2 &p_position) { - Transform2D xform = get_screen_transform(); + Transform2D xform = get_screen_transform_internal(); Vector2 gpos = xform.xform(p_position); Input::get_singleton()->warp_mouse(gpos); } @@ -2556,20 +2561,14 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) { Vector2i diff = mm->get_position() - gui.subwindow_drag_from; Size2i min_size = gui.subwindow_focused->get_min_size(); + Size2i min_size_clamped = gui.subwindow_focused->get_clamped_minimum_size(); - Size2i min_size_adjusted = min_size; - if (gui.subwindow_focused->is_wrapping_controls()) { - Size2i cms = gui.subwindow_focused->get_contents_minimum_size(); - min_size_adjusted.x = MAX(cms.x, min_size.x); - min_size_adjusted.y = MAX(cms.y, min_size.y); - } - - min_size_adjusted.x = MAX(min_size_adjusted.x, 1); - min_size_adjusted.y = MAX(min_size_adjusted.y, 1); + min_size_clamped.x = MAX(min_size_clamped.x, 1); + min_size_clamped.y = MAX(min_size_clamped.y, 1); Rect2i r = gui.subwindow_resize_from_rect; - Size2i limit = r.size - min_size_adjusted; + Size2i limit = r.size - min_size_clamped; switch (gui.subwindow_resize_mode) { case SUB_WINDOW_RESIZE_TOP_LEFT: { @@ -3288,6 +3287,10 @@ Viewport::SDFScale Viewport::get_sdf_scale() const { } Transform2D Viewport::get_screen_transform() const { + return get_screen_transform_internal(); +} + +Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) const { return get_final_transform(); } @@ -4226,14 +4229,14 @@ DisplayServer::WindowID SubViewport::get_window_id() const { return DisplayServer::INVALID_WINDOW_ID; } -Transform2D SubViewport::get_screen_transform() const { +Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) const { Transform2D container_transform; SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); if (c) { if (c->is_stretch_enabled()) { container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink())); } - container_transform = c->get_viewport()->get_screen_transform() * c->get_global_transform_with_canvas() * container_transform; + container_transform = c->get_viewport()->get_screen_transform_internal(p_absolute_position) * c->get_global_transform_with_canvas() * container_transform; } else { WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position."); } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 4144eaabb9..055fad5369 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -651,7 +651,8 @@ public: virtual bool is_size_2d_override_stretch_enabled() const { return true; } - virtual Transform2D get_screen_transform() const; + Transform2D get_screen_transform() const; + virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const; virtual Transform2D get_popup_base_transform() const { return Transform2D(); } #ifndef _3D_DISABLED @@ -780,7 +781,7 @@ public: void set_clear_mode(ClearMode p_mode); ClearMode get_clear_mode() const; - virtual Transform2D get_screen_transform() const override; + virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override; virtual Transform2D get_popup_base_transform() const override; SubViewport(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 9fcfb29ef7..44df648552 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -349,8 +349,30 @@ Size2i Window::get_size_with_decorations() const { return size; } +Size2i Window::_clamp_limit_size(const Size2i &p_limit_size) { + // Force window limits to respect size limitations of rendering server. + Size2i max_window_size = RS::get_singleton()->get_maximum_viewport_size(); + if (max_window_size != Size2i()) { + return p_limit_size.clamp(Vector2i(), max_window_size); + } else { + return p_limit_size.max(Vector2i()); + } +} + +void Window::_validate_limit_size() { + // When max_size is invalid, max_size_used falls back to respect size limitations of rendering server. + bool max_size_valid = (max_size.x > 0 || max_size.y > 0) && max_size.x >= min_size.x && max_size.y >= min_size.y; + max_size_used = max_size_valid ? max_size : RS::get_singleton()->get_maximum_viewport_size(); +} + void Window::set_max_size(const Size2i &p_max_size) { - max_size = p_max_size; + Size2i max_size_clamped = _clamp_limit_size(p_max_size); + if (max_size == max_size_clamped) { + return; + } + max_size = max_size_clamped; + + _validate_limit_size(); _update_window_size(); } @@ -359,7 +381,13 @@ Size2i Window::get_max_size() const { } void Window::set_min_size(const Size2i &p_min_size) { - min_size = p_min_size; + Size2i min_size_clamped = _clamp_limit_size(p_min_size); + if (min_size == min_size_clamped) { + return; + } + min_size = min_size_clamped; + + _validate_limit_size(); _update_window_size(); } @@ -833,49 +861,35 @@ bool Window::is_visible() const { return visible; } -void Window::_update_window_size() { - // Force window to respect size limitations of rendering server. - RenderingServer *rendering_server = RenderingServer::get_singleton(); - if (rendering_server) { - Size2i max_window_size = rendering_server->get_maximum_viewport_size(); +Size2i Window::_clamp_window_size(const Size2i &p_size) { + Size2i window_size_clamped = p_size; + Size2 minsize = get_clamped_minimum_size(); + window_size_clamped = window_size_clamped.max(minsize); - if (max_window_size != Size2i()) { - size = size.min(max_window_size); - min_size = min_size.min(max_window_size); - max_size = max_size.min(max_window_size); - } + if (max_size_used != Size2i()) { + window_size_clamped = window_size_clamped.min(max_size_used); } - Size2i size_limit; - if (wrap_controls) { - size_limit = get_contents_minimum_size(); - } + return window_size_clamped; +} - size_limit.x = MAX(size_limit.x, min_size.x); - size_limit.y = MAX(size_limit.y, min_size.y); +void Window::_update_window_size() { + Size2i size_limit = get_clamped_minimum_size(); - size.x = MAX(size_limit.x, size.x); - size.y = MAX(size_limit.y, size.y); + size = size.max(size_limit); bool reset_min_first = false; - bool max_size_valid = false; - if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) { - max_size_valid = true; + if (max_size_used != Size2i()) { + // Force window size to respect size limitations of max_size_used. + size = size.min(max_size_used); - if (size.x > max_size.x) { - size.x = max_size.x; - } - if (size_limit.x > max_size.x) { - size_limit.x = max_size.x; + if (size_limit.x > max_size_used.x) { + size_limit.x = max_size_used.x; reset_min_first = true; } - - if (size.y > max_size.y) { - size.y = max_size.y; - } - if (size_limit.y > max_size.y) { - size_limit.y = max_size.y; + if (size_limit.y > max_size_used.y) { + size_limit.y = max_size_used.y; reset_min_first = true; } } @@ -891,7 +905,7 @@ void Window::_update_window_size() { DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); } - DisplayServer::get_singleton()->window_set_max_size(max_size_valid ? max_size : Size2i(), window_id); + DisplayServer::get_singleton()->window_set_max_size(max_size_used, window_id); DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id); DisplayServer::get_singleton()->window_set_size(size, window_id); } @@ -1423,6 +1437,8 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio Rect2i popup_rect; popup_rect.size = Vector2i(MIN(size_ratio.x, p_size.x), MIN(size_ratio.y, p_size.y)); + popup_rect.size = _clamp_window_size(popup_rect.size); + if (parent_rect != Rect2()) { popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; } @@ -1446,9 +1462,7 @@ void Window::popup_centered(const Size2i &p_minsize) { } Rect2i popup_rect; - Size2 contents_minsize = _get_contents_minimum_size(); - popup_rect.size.x = MAX(p_minsize.x, contents_minsize.x); - popup_rect.size.y = MAX(p_minsize.y, contents_minsize.y); + popup_rect.size = _clamp_window_size(p_minsize); if (parent_rect != Rect2()) { popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; @@ -1476,6 +1490,7 @@ void Window::popup_centered_ratio(float p_ratio) { Rect2i popup_rect; if (parent_rect != Rect2()) { popup_rect.size = parent_rect.size * p_ratio; + popup_rect.size = _clamp_window_size(popup_rect.size); popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; } @@ -1539,6 +1554,14 @@ Size2 Window::get_contents_minimum_size() const { return _get_contents_minimum_size(); } +Size2 Window::get_clamped_minimum_size() const { + if (!wrap_controls) { + return min_size; + } + + return min_size.max(get_contents_minimum_size()); +} + void Window::grab_focus() { if (embedder) { embedder->_sub_window_grab_focus(this); @@ -2120,11 +2143,13 @@ Transform2D Window::get_final_transform() const { return window_transform * stretch_transform * global_canvas_transform; } -Transform2D Window::get_screen_transform() const { +Transform2D Window::get_screen_transform_internal(bool p_absolute_position) const { Transform2D embedder_transform; if (_get_embedder()) { embedder_transform.translate_local(get_position()); - embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform; + embedder_transform = _get_embedder()->get_screen_transform_internal(p_absolute_position) * embedder_transform; + } else if (p_absolute_position) { + embedder_transform.translate_local(get_position()); } return embedder_transform * get_final_transform(); } @@ -2389,6 +2414,7 @@ Window::Window() { RenderingServer *rendering_server = RenderingServer::get_singleton(); if (rendering_server) { max_size = rendering_server->get_maximum_viewport_size(); + max_size_used = max_size; // Update max_size_used. } theme_owner = memnew(ThemeOwner); diff --git a/scene/main/window.h b/scene/main/window.h index 9861fefc68..5359c37e9a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -135,6 +135,11 @@ private: void _clear_window(); void _update_from_window(); + Size2i max_size_used; + + Size2i _clamp_limit_size(const Size2i &p_limit_size); + Size2i _clamp_window_size(const Size2i &p_size); + void _validate_limit_size(); void _update_viewport_size(); void _update_window_size(); @@ -306,6 +311,7 @@ public: void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75); Size2 get_contents_minimum_size() const; + Size2 get_clamped_minimum_size() const; void grab_focus(); bool has_focus() const; @@ -377,7 +383,7 @@ public: // virtual Transform2D get_final_transform() const override; - virtual Transform2D get_screen_transform() const override; + virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override; virtual Transform2D get_popup_base_transform() const override; Rect2i get_parent_rect() const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 3006da5309..448e800900 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -601,9 +601,11 @@ Error ResourceLoaderText::load() { *progress = resource_current / float(resources_total); } - int_resources[id] = res; //always assign int resources - if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + int_resources[id] = res; // Always assign int resources. + if (do_assign) { + if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + } res->set_scene_unique_id(id); } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 7d9ca070b0..f9529de6dd 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -593,12 +593,10 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { bool render_shadows = p_render_data->directional_shadows.size() || p_render_data->shadows.size(); + //prepare shadow rendering if (render_shadows) { RENDER_TIMESTAMP("Render Shadows"); - } - //prepare shadow rendering - if (render_shadows) { _render_shadow_begin(); //render directional shadows |