diff options
166 files changed, 25668 insertions, 18967 deletions
diff --git a/core/io/resource.h b/core/io/resource.h index e864b371ad..9ccc247887 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -62,8 +62,6 @@ private: String path_cache; String scene_unique_id; - virtual bool _use_builtin_script() const { return true; } - #ifdef TOOLS_ENABLED uint64_t last_modified_time = 0; uint64_t import_last_modified_time = 0; diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 2ada0c556d..8a91a91b22 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -29,11 +29,11 @@ Gets the camera transform. Subclassed cameras such as [ClippedCamera3D] may provide different transforms than the [Node] transform. </description> </method> - <method name="get_cull_mask_bit" qualifiers="const"> + <method name="get_cull_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="layer" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] if the given [code]layer[/code] in the [member cull_mask] is enabled, [code]false[/code] otherwise. + Returns whether or not the specified layer of the [member cull_mask] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> <method name="get_frustum" qualifiers="const"> @@ -92,12 +92,12 @@ Returns a 3D position in world space, that is the result of projecting a point on the [Viewport] rectangle by the camera projection. This is useful for casting rays in the form of (origin, normal) for object intersection or picking. </description> </method> - <method name="set_cull_mask_bit"> + <method name="set_cull_mask_value"> <return type="void" /> - <argument index="0" name="layer" type="int" /> - <argument index="1" name="enable" type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> <description> - Enables or disables the given [code]layer[/code] in the [member cull_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member cull_mask], given a [code]layer_number[/code] between 1 and 20. </description> </method> <method name="set_frustum"> diff --git a/doc/classes/ClippedCamera3D.xml b/doc/classes/ClippedCamera3D.xml index 1a76412826..1a0d3499cd 100644 --- a/doc/classes/ClippedCamera3D.xml +++ b/doc/classes/ClippedCamera3D.xml @@ -35,12 +35,11 @@ Returns the distance the camera has been offset due to a collision. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] if the specified bit index is on. - [b]Note:[/b] Bit indices range from 0-19. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="remove_exception"> @@ -57,13 +56,12 @@ Removes a collision exception with the specified [RID]. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets the specified bit index to the [code]value[/code]. - [b]Note:[/b] Bit indices range from 0-19. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml index eb1dad14f2..47078330f3 100644 --- a/doc/classes/CodeEdit.xml +++ b/doc/classes/CodeEdit.xml @@ -645,7 +645,7 @@ The tint of text outline of the [CodeEdit]. </theme_item> <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 0.5)"> - Sets the font [Color] when [member TextEdit.readonly] is enabled. + Sets the font [Color] when [member TextEdit.editable] is disabled. </theme_item> <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)"> Sets the [Color] of the selected text. [member TextEdit.override_selected_font_color] has to be enabled. @@ -669,7 +669,7 @@ The size of the text outline. </theme_item> <theme_item name="read_only" data_type="style" type="StyleBox"> - Sets the [StyleBox] when [member TextEdit.readonly] is enabled. + Sets the [StyleBox] when [member TextEdit.editable] is disabled. </theme_item> <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.49, 0.49, 0.49, 1)"> Sets the highlight [Color] of text selections. diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index 6bb756ea2c..7129c72e7c 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -25,18 +25,18 @@ Creates a new shape owner for the given object. Returns [code]owner_id[/code] of the new owner for future reference. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_layer] is set. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_mask] is set. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_rid" qualifiers="const"> @@ -79,22 +79,20 @@ Removes the given shape owner. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_layer]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_mask]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="shape_find_owner" qualifiers="const"> diff --git a/doc/classes/CollisionObject3D.xml b/doc/classes/CollisionObject3D.xml index 0210f6297f..f9151a2c2f 100644 --- a/doc/classes/CollisionObject3D.xml +++ b/doc/classes/CollisionObject3D.xml @@ -27,18 +27,18 @@ Creates a new shape owner for the given object. Returns [code]owner_id[/code] of the new owner for future reference. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_layer] is set. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_mask] is set. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_rid" qualifiers="const"> @@ -67,22 +67,20 @@ Removes the given shape owner. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_layer]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_mask]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="shape_find_owner" qualifiers="const"> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 89a394ef6c..e476949360 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -30,11 +30,11 @@ Initializes the navigation mesh by setting the vertices and indices according to a [Mesh]. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether the specified [code]bit[/code] of the [member geometry/collision_mask] is set. + Returns whether or not the specified layer of the [member geometry/collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_polygon"> @@ -56,13 +56,12 @@ Returns a [PackedVector3Array] containing all the vertices being used to create the polygons. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the [member geometry/collision_mask]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the [member geometry/collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member geometry/collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="set_vertices"> diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml index 150bfd9257..cc4bddc229 100644 --- a/doc/classes/OccluderInstance3D.xml +++ b/doc/classes/OccluderInstance3D.xml @@ -7,17 +7,19 @@ <tutorials> </tutorials> <methods> - <method name="get_bake_mask_bit" qualifiers="const"> + <method name="get_bake_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="layer" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> + Returns whether or not the specified layer of the [member bake_mask] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> - <method name="set_bake_mask_bit"> + <method name="set_bake_mask_value"> <return type="void" /> - <argument index="0" name="layer" type="int" /> - <argument index="1" name="enabled" type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> <description> + Based on [code]value[/code], enables or disables the specified layer in the [member bake_mask], given a [code]layer_number[/code] between 1 and 20. </description> </method> </methods> diff --git a/doc/classes/PhysicsDirectSpaceState2D.xml b/doc/classes/PhysicsDirectSpaceState2D.xml index 2a32bc1cb9..e84b3e0e49 100644 --- a/doc/classes/PhysicsDirectSpaceState2D.xml +++ b/doc/classes/PhysicsDirectSpaceState2D.xml @@ -47,7 +47,7 @@ <argument index="0" name="point" type="Vector2" /> <argument index="1" name="max_results" type="int" default="32" /> <argument index="2" name="exclude" type="Array" default="[]" /> - <argument index="3" name="collision_layer" type="int" default="2147483647" /> + <argument index="3" name="collision_mask" type="int" default="2147483647" /> <argument index="4" name="collide_with_bodies" type="bool" default="true" /> <argument index="5" name="collide_with_areas" type="bool" default="false" /> <description> @@ -57,7 +57,7 @@ [code]metadata[/code]: The intersecting shape's metadata. This metadata is different from [method Object.get_meta], and is set with [method PhysicsServer2D.shape_set_data]. [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 check in, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. + 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, 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> @@ -67,7 +67,7 @@ <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_layer" type="int" default="2147483647" /> + <argument index="4" name="collision_mask" type="int" default="2147483647" /> <argument index="5" name="collide_with_bodies" type="bool" default="true" /> <argument index="6" name="collide_with_areas" type="bool" default="false" /> <description> @@ -78,7 +78,7 @@ <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_layer" type="int" default="2147483647" /> + <argument index="3" name="collision_mask" type="int" default="2147483647" /> <argument index="4" name="collide_with_bodies" type="bool" default="true" /> <argument index="5" name="collide_with_areas" type="bool" default="false" /> <description> @@ -91,7 +91,7 @@ [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 check in, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. + 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, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. </description> </method> <method name="intersect_shape"> diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml index a6bfc8754d..13db50a2c7 100644 --- a/doc/classes/PhysicsDirectSpaceState3D.xml +++ b/doc/classes/PhysicsDirectSpaceState3D.xml @@ -59,7 +59,7 @@ [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 check in, or booleans to determine if the ray should collide with [PhysicsBody3D]s or [Area3D]s, respectively. + 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, or booleans to determine if the ray should collide with [PhysicsBody3D]s or [Area3D]s, respectively. </description> </method> <method name="intersect_shape"> diff --git a/doc/classes/PhysicsShapeQueryParameters2D.xml b/doc/classes/PhysicsShapeQueryParameters2D.xml index 229a40638a..8b006c68e7 100644 --- a/doc/classes/PhysicsShapeQueryParameters2D.xml +++ b/doc/classes/PhysicsShapeQueryParameters2D.xml @@ -17,8 +17,8 @@ <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_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="2147483647"> - The physics layer(s) the query will take into account (as a bitmask). 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 name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="2147483647"> + 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. diff --git a/doc/classes/PhysicsShapeQueryParameters3D.xml b/doc/classes/PhysicsShapeQueryParameters3D.xml index 9ca892acb3..de9b623591 100644 --- a/doc/classes/PhysicsShapeQueryParameters3D.xml +++ b/doc/classes/PhysicsShapeQueryParameters3D.xml @@ -18,7 +18,7 @@ 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="2147483647"> - The physics layer(s) the query will take into account (as a bitmask). 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. + 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. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 24caceec1c..f769ace836 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -777,9 +777,6 @@ <member name="internationalization/rendering/text_driver" type="String" setter="" getter="" default=""""> Specifies the [TextServer] to use. If left empty, the default will be used. </member> - <member name="layer_names/2d_navigation/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D navigation layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/2d_navigation/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -855,6 +852,9 @@ <member name="layer_names/2d_navigation/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/2d_navigation/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 2D navigation layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/2d_navigation/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -873,9 +873,6 @@ <member name="layer_names/2d_navigation/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/2d_physics/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D physics layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -951,6 +948,9 @@ <member name="layer_names/2d_physics/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/2d_physics/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 2D physics layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/2d_physics/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -969,9 +969,6 @@ <member name="layer_names/2d_physics/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/2d_render/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D render layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/2d_render/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1008,6 +1005,9 @@ <member name="layer_names/2d_render/layer_2" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 2. If left empty, the layer will display as "Layer 2". </member> + <member name="layer_names/2d_render/layer_20" type="String" setter="" getter="" default=""""> + Optional name for the 2D render layer 20. If left empty, the layer will display as "Layer 20". + </member> <member name="layer_names/2d_render/layer_3" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 3. If left empty, the layer will display as "Layer 3". </member> @@ -1029,9 +1029,6 @@ <member name="layer_names/2d_render/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/3d_navigation/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 3D navigation layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/3d_navigation/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1107,6 +1104,9 @@ <member name="layer_names/3d_navigation/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/3d_navigation/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 3D navigation layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/3d_navigation/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -1125,9 +1125,6 @@ <member name="layer_names/3d_navigation/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/3d_physics/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 3D physics layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1203,6 +1200,9 @@ <member name="layer_names/3d_physics/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/3d_physics/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 3D physics layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/3d_physics/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -1221,9 +1221,6 @@ <member name="layer_names/3d_physics/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/3d_render/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D render layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/3d_render/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 3D render layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1260,6 +1257,9 @@ <member name="layer_names/3d_render/layer_2" type="String" setter="" getter="" default=""""> Optional name for the 3D render layer 2. If left empty, the layer will display as "Layer 2". </member> + <member name="layer_names/3d_render/layer_20" type="String" setter="" getter="" default=""""> + Optional name for the 3D render layer 20. If left empty, the layer will display as "Layer 20". + </member> <member name="layer_names/3d_render/layer_3" type="String" setter="" getter="" default=""""> Optional name for the 3D render layer 3. If left empty, the layer will display as "Layer 3". </member> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index d8a5fc8508..1d32db8078 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -53,11 +53,11 @@ Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_collision_normal" qualifiers="const"> @@ -93,12 +93,12 @@ Removes a collision exception so the ray does report collisions with the specified [RID]. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets or clears individual bits on the collision mask. This makes selecting the areas scanned easier. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml index 4e8eb960db..8628ab7dac 100644 --- a/doc/classes/RayCast3D.xml +++ b/doc/classes/RayCast3D.xml @@ -55,12 +55,11 @@ Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] if the bit index passed is turned on. - [b]Note:[/b] Bit indices range from 0-19. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_collision_normal" qualifiers="const"> @@ -96,13 +95,12 @@ Removes a collision exception so the ray does report collisions with the specified [RID]. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets the bit index passed to the [code]value[/code] passed. - [b]Note:[/b] Bit indexes range from 0-19. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index 53bd7e67bf..ddfc14ceac 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -23,18 +23,18 @@ Returns an array of nodes that were added as collision exceptions for this body. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_physics_rid" qualifiers="const"> @@ -49,20 +49,20 @@ Removes a body from the list of bodies that this body can't collide with. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 7b34b3c461..c31b7b953e 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -13,93 +13,99 @@ <method name="_backspace" qualifiers="virtual"> <return type="void" /> <description> - A virtual method that is called whenever backspace is triggered. + Override this method to define what happens when the user presses the backspace key. + </description> + </method> + <method name="_copy" qualifiers="virtual"> + <return type="void" /> + <description> + Override this method to define what happens when the user performs a copy operation. + </description> + </method> + <method name="_cut" qualifiers="virtual"> + <return type="void" /> + <description> + Override this method to define what happens when the user performs a cut operation. </description> </method> <method name="_handle_unicode_input" qualifiers="virtual"> <return type="void" /> <argument index="0" name="unicode" type="int" /> <description> + Override this method to define what happens when the types in the provided key [code]unicode[/code]. </description> </method> - <method name="add_gutter"> + <method name="_paste" qualifiers="virtual"> <return type="void" /> - <argument index="0" name="at" type="int" default="-1" /> <description> + Override this method to define what happens when the user performs a paste operation. </description> </method> - <method name="backspace"> + <method name="add_gutter"> <return type="void" /> + <argument index="0" name="at" type="int" default="-1" /> <description> - Causes the [TextEdit] to perform a backspace. + Register a new gutter to this [TextEdit]. Use [code]at[/code] to have a specific gutter order. A value of [code]-1[/code] appends the gutter to the right. </description> </method> - <method name="center_viewport_to_cursor"> + <method name="adjust_viewport_to_caret"> <return type="void" /> <description> - Centers the viewport on the line the editing cursor is at. This also resets the [member scroll_horizontal] value to [code]0[/code]. + Adjust the viewport so the caret is visible. </description> </method> - <method name="clear_opentype_features"> + <method name="backspace"> <return type="void" /> <description> - Removes all OpenType features. + Called when the user presses the backspace key. Can be overridden with [method _backspace]. </description> </method> - <method name="clear_undo_history"> + <method name="begin_complex_operation"> <return type="void" /> <description> - Clears the undo history. + Starts a multipart edit. All edits will be treated as one action until [method end_complex_operation] is called. </description> </method> - <method name="copy"> + <method name="center_viewport_to_caret"> <return type="void" /> <description> - Copy's the current text selection. + Centers the viewport on the line the editing caret is at. This also resets the [member scroll_horizontal] value to [code]0[/code]. </description> </method> - <method name="cursor_get_column" qualifiers="const"> - <return type="int" /> + <method name="clear"> + <return type="void" /> <description> - Returns the column the editing cursor is at. + Performs a full reset of [TextEdit], including undo history. </description> </method> - <method name="cursor_get_line" qualifiers="const"> - <return type="int" /> + <method name="clear_opentype_features"> + <return type="void" /> <description> - Returns the line the editing cursor is at. + Removes all OpenType features. </description> </method> - <method name="cursor_set_column"> + <method name="clear_undo_history"> <return type="void" /> - <argument index="0" name="column" type="int" /> - <argument index="1" name="adjust_viewport" type="bool" default="true" /> <description> - Moves the cursor at the specified [code]column[/code] index. - If [code]adjust_viewport[/code] is set to [code]true[/code], the viewport will center at the cursor position after the move occurs. + Clears the undo history. </description> </method> - <method name="cursor_set_line"> + <method name="copy"> <return type="void" /> - <argument index="0" name="line" type="int" /> - <argument index="1" name="adjust_viewport" type="bool" default="true" /> - <argument index="2" name="can_be_hidden" type="bool" default="true" /> - <argument index="3" name="wrap_index" type="int" default="0" /> <description> - Moves the cursor at the specified [code]line[/code] index. - If [code]adjust_viewport[/code] is set to [code]true[/code], the viewport will center at the cursor position after the move occurs. - If [code]can_be_hidden[/code] is set to [code]true[/code], the specified [code]line[/code] can be hidden. + Copies the current text selection. Can be overridden with [method _copy]. </description> </method> <method name="cut"> <return type="void" /> <description> - Cut's the current selection. + Cut's the current selection. Can be overridden with [method _cut]. </description> </method> <method name="delete_selection"> <return type="void" /> <description> + Deletes the selected text. </description> </method> <method name="deselect"> @@ -108,46 +114,99 @@ Deselects the current selection. </description> </method> + <method name="end_complex_operation"> + <return type="void" /> + <description> + Ends a multipart edit, started with [method begin_complex_operation]. If called outside a complex operation, the current operation is pushed onto the undo/redo stack. + </description> + </method> + <method name="get_caret_column" qualifiers="const"> + <return type="int" /> + <description> + Returns the column the editing caret is at. + </description> + </method> <method name="get_caret_draw_pos" qualifiers="const"> <return type="Vector2" /> <description> - Gets the caret pixel draw position. + Returns the caret pixel draw position. + </description> + </method> + <method name="get_caret_line" qualifiers="const"> + <return type="int" /> + <description> + Returns the line the editing caret is on. + </description> + </method> + <method name="get_caret_wrap_index" qualifiers="const"> + <return type="int" /> + <description> + Returns the wrap index the editing caret is on. </description> </method> <method name="get_first_non_whitespace_column" qualifiers="const"> <return type="int" /> <argument index="0" name="line" type="int" /> <description> + Returns the first column containing a non-whitespace character. + </description> + </method> + <method name="get_first_visible_line" qualifiers="const"> + <return type="int" /> + <description> + Returns the first visible line. </description> </method> <method name="get_gutter_count" qualifiers="const"> <return type="int" /> <description> + Returns the total amount of gutters registered. </description> </method> <method name="get_gutter_name" qualifiers="const"> <return type="String" /> <argument index="0" name="gutter" type="int" /> <description> + Returns the name of the gutter at the given index. </description> </method> <method name="get_gutter_type" qualifiers="const"> <return type="int" enum="TextEdit.GutterType" /> <argument index="0" name="gutter" type="int" /> <description> + Returns the type of the gutter at the given index. </description> </method> <method name="get_gutter_width" qualifiers="const"> <return type="int" /> <argument index="0" name="gutter" type="int" /> <description> + Returns the width of the gutter at the given index. </description> </method> <method name="get_indent_level" qualifiers="const"> <return type="int" /> <argument index="0" name="line" type="int" /> <description> - Returns the indent level of a specific line. + Returns the amount of spaces and [code]tab * tab_size[/code] before the first char. + </description> + </method> + <method name="get_last_full_visible_line" qualifiers="const"> + <return type="int" /> + <description> + Return the last visible line. Use [method get_last_full_visible_line_wrap_index] for the wrap index. + </description> + </method> + <method name="get_last_full_visible_line_wrap_index" qualifiers="const"> + <return type="int" /> + <description> + Returns the last visible wrap index of the last visible line. + </description> + </method> + <method name="get_last_unhidden_line" qualifiers="const"> + <return type="int" /> + <description> + Returns the last unhidden line in the entire [TextEdit]. </description> </method> <method name="get_line" qualifiers="const"> @@ -157,13 +216,20 @@ Returns the text of a specific line. </description> </method> - <method name="get_line_background_color"> + <method name="get_line_background_color" qualifiers="const"> <return type="Color" /> <argument index="0" name="line" type="int" /> <description> Returns the current background color of the line. [code]Color(0, 0, 0, 0)[/code] is returned if no color is set. </description> </method> + <method name="get_line_column_at_pos" qualifiers="const"> + <return type="Vector2i" /> + <argument index="0" name="position" type="Vector2i" /> + <description> + Returns the line and column at the given position. In the returned vector, [code]x[/code] is the column, [code]y[/code] is the line. + </description> + </method> <method name="get_line_count" qualifiers="const"> <return type="int" /> <description> @@ -175,13 +241,15 @@ <argument index="0" name="line" type="int" /> <argument index="1" name="gutter" type="int" /> <description> + Returns the icon currently in [code]gutter[/code] at [code]line[/code]. </description> </method> - <method name="get_line_gutter_item_color"> + <method name="get_line_gutter_item_color" qualifiers="const"> <return type="Color" /> <argument index="0" name="line" type="int" /> <argument index="1" name="gutter" type="int" /> <description> + Returns the color currently in [code]gutter[/code] at [code]line[/code]. </description> </method> <method name="get_line_gutter_metadata" qualifiers="const"> @@ -189,6 +257,7 @@ <argument index="0" name="line" type="int" /> <argument index="1" name="gutter" type="int" /> <description> + Returns the metadata currently in [code]gutter[/code] at [code]line[/code]. </description> </method> <method name="get_line_gutter_text" qualifiers="const"> @@ -196,6 +265,49 @@ <argument index="0" name="line" type="int" /> <argument index="1" name="gutter" type="int" /> <description> + Returns the text currently in [code]gutter[/code] at [code]line[/code]. + </description> + </method> + <method name="get_line_height" qualifiers="const"> + <return type="int" /> + <description> + Returns the height of a largest line. + </description> + </method> + <method name="get_line_width" qualifiers="const"> + <return type="int" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="wrap_index" type="int" default="-1" /> + <description> + Returns the width in pixels of the [code]wrap_index[/code] on [code]line[/code]. + </description> + </method> + <method name="get_line_wrap_count" qualifiers="const"> + <return type="int" /> + <argument index="0" name="line" type="int" /> + <description> + Returns the number of times the given line is wrapped. + </description> + </method> + <method name="get_line_wrap_index_at_column" qualifiers="const"> + <return type="int" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="column" type="int" /> + <description> + Returns the wrap index of the given line column. + </description> + </method> + <method name="get_line_wrapped_text" qualifiers="const"> + <return type="PackedStringArray" /> + <argument index="0" name="line" type="int" /> + <description> + Returns an array of [String]s representing each wrapped index. + </description> + </method> + <method name="get_local_mouse_pos" qualifiers="const"> + <return type="Vector2" /> + <description> + Returns the local mouse position adjusted for the text direction. </description> </method> <method name="get_menu" qualifiers="const"> @@ -204,6 +316,36 @@ Returns the [PopupMenu] of this [TextEdit]. By default, this menu is displayed when right-clicking on the [TextEdit]. </description> </method> + <method name="get_minimap_line_at_pos" qualifiers="const"> + <return type="int" /> + <argument index="0" name="position" type="Vector2i" /> + <description> + Returns the equivalent minimap line at [code]position[/code] + </description> + </method> + <method name="get_minimap_visible_lines" qualifiers="const"> + <return type="int" /> + <description> + Returns the total amount of lines that can be draw on the minimap. + </description> + </method> + <method name="get_next_visible_line_index_offset_from" qualifiers="const"> + <return type="Vector2i" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="wrap_index" type="int" /> + <argument index="2" name="visible_amount" type="int" /> + <description> + Similar to [method get_next_visible_line_offset_from], but takes into account the line wrap indexes. In the returned vector, [code]x[/code] is the line, [code]y[/code] is the wrap index. + </description> + </method> + <method name="get_next_visible_line_offset_from" qualifiers="const"> + <return type="int" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="visible_amount" type="int" /> + <description> + Returns the count to the next visible line from [code]line[/code] to [code]line + visible_amount[/code]. Can also count backwards. For example if a [TextEdit] has 5 lines with lines 2 and 3 hidden, calling this with [code]line = 1, visible_amount = 1[/code] would return 3. + </description> + </method> <method name="get_opentype_feature" qualifiers="const"> <return type="int" /> <argument index="0" name="tag" type="String" /> @@ -211,9 +353,30 @@ Returns OpenType feature [code]tag[/code]. </description> </method> + <method name="get_saved_version" qualifiers="const"> + <return type="int" /> + <description> + Returns the last tagged saved version from [method tag_saved_version] + </description> + </method> + <method name="get_scroll_pos_for_line" qualifiers="const"> + <return type="float" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="wrap_index" type="int" default="0" /> + <description> + Returns the scroll position for [code]wrap_index[/code] of [code]line[/code]. + </description> + </method> + <method name="get_selected_text" qualifiers="const"> + <return type="String" /> + <description> + Returns the text inside the selection. + </description> + </method> <method name="get_selection_column" qualifiers="const"> <return type="int" /> <description> + Returns the original start column of the selection. </description> </method> <method name="get_selection_from_column" qualifiers="const"> @@ -231,17 +394,13 @@ <method name="get_selection_line" qualifiers="const"> <return type="int" /> <description> + Returns the original start line of the selection. </description> </method> <method name="get_selection_mode" qualifiers="const"> <return type="int" enum="TextEdit.SelectionMode" /> <description> - </description> - </method> - <method name="get_selection_text" qualifiers="const"> - <return type="String" /> - <description> - Returns the text inside the selection. + Returns the current selection mode. </description> </method> <method name="get_selection_to_column" qualifiers="const"> @@ -265,6 +424,19 @@ <method name="get_total_gutter_width" qualifiers="const"> <return type="int" /> <description> + Returns the total width of all gutters and internal padding. + </description> + </method> + <method name="get_total_visible_line_count" qualifiers="const"> + <return type="int" /> + <description> + Returns the total amount of lines that could be draw. + </description> + </method> + <method name="get_version" qualifiers="const"> + <return type="int" /> + <description> + Returns the current version of the [TextEdit]. The version is a count of recorded operations by the undo/redo history. </description> </method> <method name="get_visible_line_count" qualifiers="const"> @@ -273,17 +445,44 @@ Returns the number of visible lines, including wrapped text. </description> </method> - <method name="get_word_under_cursor" qualifiers="const"> + <method name="get_word_at_pos" qualifiers="const"> <return type="String" /> + <argument index="0" name="position" type="Vector2" /> + <description> + Returns the word at [code]position[/code]. + </description> + </method> + <method name="get_word_under_caret" qualifiers="const"> + <return type="String" /> + <description> + Returns a [String] text with the word under the caret's location. + </description> + </method> + <method name="has_ime_text" qualifiers="const"> + <return type="bool" /> + <description> + Returns if the user has IME text. + </description> + </method> + <method name="has_selection" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the user has selected text. + </description> + </method> + <method name="insert_line_at"> + <return type="void" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="text" type="String" /> <description> - Returns a [String] text with the word under the caret (text cursor) location. + Inserts a new line with [code]text[/code] at [code]line[/code]. </description> </method> - <method name="insert_text_at_cursor"> + <method name="insert_text_at_caret"> <return type="void" /> <argument index="0" name="text" type="String" /> <description> - Insert the specified text at the cursor position. + Insert the specified text at the caret position. </description> </method> <method name="is_caret_visible" qualifiers="const"> @@ -295,24 +494,28 @@ <method name="is_dragging_cursor" qualifiers="const"> <return type="bool" /> <description> + Returns [code]true[/code] if the user is dragging their mouse for scrolling or selecting. </description> </method> <method name="is_gutter_clickable" qualifiers="const"> <return type="bool" /> <argument index="0" name="gutter" type="int" /> <description> + Returns whether the gutter is clickable. </description> </method> <method name="is_gutter_drawn" qualifiers="const"> <return type="bool" /> <argument index="0" name="gutter" type="int" /> <description> + Returns whether the gutter is currently drawn. </description> </method> <method name="is_gutter_overwritable" qualifiers="const"> <return type="bool" /> <argument index="0" name="gutter" type="int" /> <description> + Returns whether the gutter is overwritable. </description> </method> <method name="is_line_gutter_clickable" qualifiers="const"> @@ -320,6 +523,14 @@ <argument index="0" name="line" type="int" /> <argument index="1" name="gutter" type="int" /> <description> + Returns whether the gutter on the given line is clickable. + </description> + </method> + <method name="is_line_wrapped" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="line" type="int" /> + <description> + Returns if the given line is wrapped. </description> </method> <method name="is_menu_visible" qualifiers="const"> @@ -328,10 +539,10 @@ Returns whether the menu is visible. Use this instead of [code]get_menu().visible[/code] to improve performance (so the creation of the menu is avoided). </description> </method> - <method name="is_selection_active" qualifiers="const"> + <method name="is_overtype_mode_enabled" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the selection is active. + Returns whether the user is in overtype mode. </description> </method> <method name="menu_option"> @@ -346,12 +557,13 @@ <argument index="0" name="from_line" type="int" /> <argument index="1" name="to_line" type="int" /> <description> + Merge the gutters from [code]from_line[/code] into [code]to_line[/code]. Only overwritable gutters will be copied. </description> </method> <method name="paste"> <return type="void" /> <description> - Paste the current selection. + Paste at the current location. Can be overridden with [method _paste]. </description> </method> <method name="redo"> @@ -364,32 +576,44 @@ <return type="void" /> <argument index="0" name="gutter" type="int" /> <description> + Removes the gutter from this [TextEdit]. + </description> + </method> + <method name="remove_text"> + <return type="void" /> + <argument index="0" name="from_line" type="int" /> + <argument index="1" name="from_column" type="int" /> + <argument index="2" name="to_line" type="int" /> + <argument index="3" name="to_column" type="int" /> + <description> + Removes text between the given positions. + [b]Note:[/b]This does not adjust the caret or selection, which as a result it can end up in an invalid position. </description> </method> <method name="search" qualifiers="const"> - <return type="Dictionary" /> - <argument index="0" name="key" type="String" /> + <return type="Vector2i" /> + <argument index="0" name="text" type="String" /> <argument index="1" name="flags" type="int" /> <argument index="2" name="from_line" type="int" /> - <argument index="3" name="from_column" type="int" /> + <argument index="3" name="from_colum" type="int" /> <description> Perform a search inside the text. Search flags can be specified in the [enum SearchFlags] enum. - Returns an empty [code]Dictionary[/code] if no result was found. Otherwise, returns a [code]Dictionary[/code] containing [code]line[/code] and [code]column[/code] entries, e.g: + In the returned vector, [code]x[/code] is the column, [code]y[/code] is the line. If no results are found, both are equal to [code]-1[/code]. [codeblocks] [gdscript] var result = search("print", SEARCH_WHOLE_WORDS, 0, 0) - if !result.empty(): + if result.x != -1: # Result found. - var line_number = result.line - var column_number = result.column + var line_number = result.y + var column_number = result.x [/gdscript] [csharp] - int[] result = Search("print", (uint)TextEdit.SearchFlags.WholeWords, 0, 0); + Vector2i result = Search("print", (uint)TextEdit.SearchFlags.WholeWords, 0, 0); if (result.Length > 0) { // Result found. - int lineNumber = result[(int)TextEdit.SearchResult.Line]; - int columnNumber = result[(int)TextEdit.SearchResult.Column]; + int lineNumber = result.y; + int columnNumber = result.x; } [/csharp] [/codeblocks] @@ -413,11 +637,39 @@ If [member selecting_enabled] is [code]false[/code], no selection will occur. </description> </method> + <method name="select_word_under_caret"> + <return type="void" /> + <description> + Selects the word under the caret. + </description> + </method> + <method name="set_caret_column"> + <return type="void" /> + <argument index="0" name="column" type="int" /> + <argument index="1" name="adjust_viewport" type="bool" default="true" /> + <description> + Moves the caret to the specified [code]column[/code] index. + If [code]adjust_viewport[/code] is [code]true[/code], the viewport will center at the caret position after the move occurs. + </description> + </method> + <method name="set_caret_line"> + <return type="void" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="adjust_viewport" type="bool" default="true" /> + <argument index="2" name="can_be_hidden" type="bool" default="true" /> + <argument index="3" name="wrap_index" type="int" default="0" /> + <description> + Moves the caret to the specified [code]line[/code] index. + If [code]adjust_viewport[/code] is [code]true[/code], the viewport will center at the caret position after the move occurs. + If [code]can_be_hidden[/code] is [code]true[/code], the specified [code]line[/code] can be hidden. + </description> + </method> <method name="set_gutter_clickable"> <return type="void" /> <argument index="0" name="gutter" type="int" /> <argument index="1" name="clickable" type="bool" /> <description> + Sets the gutter as clickable. This will change the mouse cursor to a pointing hand when hovering over the gutter. </description> </method> <method name="set_gutter_custom_draw"> @@ -426,6 +678,7 @@ <argument index="1" name="object" type="Object" /> <argument index="2" name="callback" type="StringName" /> <description> + Set a custom draw method for the gutter. The callback method must take the following args: [code]line: int, gutter: int, Area: Rect2[/code]. </description> </method> <method name="set_gutter_draw"> @@ -433,6 +686,7 @@ <argument index="0" name="gutter" type="int" /> <argument index="1" name="draw" type="bool" /> <description> + Sets whether the gutter should be drawn. </description> </method> <method name="set_gutter_name"> @@ -440,6 +694,7 @@ <argument index="0" name="gutter" type="int" /> <argument index="1" name="name" type="String" /> <description> + Sets the name of the gutter. </description> </method> <method name="set_gutter_overwritable"> @@ -447,6 +702,7 @@ <argument index="0" name="gutter" type="int" /> <argument index="1" name="overwritable" type="bool" /> <description> + Sets the gutter to overwritable. See [method merge_gutters]. </description> </method> <method name="set_gutter_type"> @@ -454,6 +710,7 @@ <argument index="0" name="gutter" type="int" /> <argument index="1" name="type" type="int" enum="TextEdit.GutterType" /> <description> + Sets the type of gutter. </description> </method> <method name="set_gutter_width"> @@ -461,6 +718,7 @@ <argument index="0" name="gutter" type="int" /> <argument index="1" name="width" type="int" /> <description> + Set the width of the gutter. </description> </method> <method name="set_line"> @@ -471,6 +729,30 @@ Sets the text for a specific line. </description> </method> + <method name="set_line_as_center_visible"> + <return type="void" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="wrap_index" type="int" default="0" /> + <description> + Positions the [code]wrap_index[/code] of [code]line[/code] at the center of the viewport. + </description> + </method> + <method name="set_line_as_first_visible"> + <return type="void" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="wrap_index" type="int" default="0" /> + <description> + Positions the [code]wrap_index[/code] of [code]line[/code] at the top of the viewport. + </description> + </method> + <method name="set_line_as_last_visible"> + <return type="void" /> + <argument index="0" name="line" type="int" /> + <argument index="1" name="wrap_index" type="int" default="0" /> + <description> + Positions the [code]wrap_index[/code] of [code]line[/code] at the bottom of the viewport. + </description> + </method> <method name="set_line_background_color"> <return type="void" /> <argument index="0" name="line" type="int" /> @@ -485,6 +767,7 @@ <argument index="1" name="gutter" type="int" /> <argument index="2" name="clickable" type="bool" /> <description> + Sets the [code]gutter[/code] on [code]line[/code] as clickable. </description> </method> <method name="set_line_gutter_icon"> @@ -493,6 +776,7 @@ <argument index="1" name="gutter" type="int" /> <argument index="2" name="icon" type="Texture2D" /> <description> + Sets the icon for [code]gutter[/code] on [code]line[/code]. </description> </method> <method name="set_line_gutter_item_color"> @@ -501,6 +785,7 @@ <argument index="1" name="gutter" type="int" /> <argument index="2" name="color" type="Color" /> <description> + Sets the color for [code]gutter[/code] on [code]line[/code]. </description> </method> <method name="set_line_gutter_metadata"> @@ -509,6 +794,7 @@ <argument index="1" name="gutter" type="int" /> <argument index="2" name="metadata" type="Variant" /> <description> + Sets the metadata for [code]gutter[/code] on [code]line[/code]. </description> </method> <method name="set_line_gutter_text"> @@ -517,6 +803,7 @@ <argument index="1" name="gutter" type="int" /> <argument index="2" name="text" type="String" /> <description> + Sets the text for [code]gutter[/code] on [code]line[/code]. </description> </method> <method name="set_opentype_feature"> @@ -527,12 +814,34 @@ Sets OpenType feature [code]tag[/code]. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. </description> </method> + <method name="set_overtype_mode_enabled"> + <return type="void" /> + <argument index="0" name="enabled" type="bool" /> + <description> + If [code]true[/code], sets the user into overtype mode. When the user types in this mode, it will override existing text. + </description> + </method> + <method name="set_search_flags"> + <return type="void" /> + <argument index="0" name="flags" type="int" /> + <description> + Sets the search flags. This is used with [method set_search_text] to highlight occurrences of the searched text. Search flags can be specified from the [enum SearchFlags] enum. + </description> + </method> + <method name="set_search_text"> + <return type="void" /> + <argument index="0" name="search_text" type="String" /> + <description> + Sets the search text. See [method set_search_flags]. + </description> + </method> <method name="set_selection_mode"> <return type="void" /> <argument index="0" name="mode" type="int" enum="TextEdit.SelectionMode" /> <argument index="1" name="line" type="int" default="-1" /> <argument index="2" name="column" type="int" default="-1" /> <description> + Sets the current selection mode. </description> </method> <method name="set_tab_size"> @@ -542,6 +851,29 @@ Sets the tab size for the [TextEdit] to use. </description> </method> + <method name="set_tooltip_request_func"> + <return type="void" /> + <argument index="0" name="object" type="Object" /> + <argument index="1" name="callback" type="StringName" /> + <argument index="2" name="data" type="Variant" /> + <description> + Provide custom tooltip text. The callback method must take the following args: [code]hovered_word: String, data: Variant[/code] + </description> + </method> + <method name="swap_lines"> + <return type="void" /> + <argument index="0" name="from_line" type="int" /> + <argument index="1" name="to_line" type="int" /> + <description> + Swaps the two lines. + </description> + </method> + <method name="tag_saved_version"> + <return type="void" /> + <description> + Tag the current version as saved. + </description> + </method> <method name="undo"> <return type="void" /> <description> @@ -550,24 +882,23 @@ </method> </methods> <members> - <member name="caret_blink" type="bool" setter="cursor_set_blink_enabled" getter="cursor_get_blink_enabled" default="false"> - If [code]true[/code], the caret (visual cursor) blinks. + <member name="caret_blink" type="bool" setter="set_caret_blink_enabled" getter="is_caret_blink_enabled" default="false"> + Sets if the caret should blink. </member> - <member name="caret_blink_speed" type="float" setter="cursor_set_blink_speed" getter="cursor_get_blink_speed" default="0.65"> + <member name="caret_blink_speed" type="float" setter="set_caret_blink_speed" getter="get_caret_blink_speed" default="0.65"> Duration (in seconds) of a caret's blinking cycle. </member> - <member name="caret_block_mode" type="bool" setter="cursor_set_block_mode" getter="cursor_is_block_mode" default="false"> - If [code]true[/code], the caret displays as a rectangle. - If [code]false[/code], the caret displays as a bar. - </member> - <member name="caret_mid_grapheme" type="bool" setter="set_mid_grapheme_caret_enabled" getter="get_mid_grapheme_caret_enabled" default="false"> + <member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false"> Allow moving caret, selecting and removing the individual composite character components. Note: [kbd]Backspace[/kbd] is always removing individual composite character components. </member> - <member name="caret_moving_by_right_click" type="bool" setter="set_right_click_moves_caret" getter="is_right_click_moving_caret" default="true"> - If [code]true[/code], a right-click moves the cursor at the mouse position before displaying the context menu. + <member name="caret_move_on_right_click" type="bool" setter="set_move_caret_on_right_click_enabled" getter="is_move_caret_on_right_click_enabled" default="true"> + If [code]true[/code], a right-click moves the caret at the mouse position before displaying the context menu. If [code]false[/code], the context menu disregards mouse location. </member> + <member name="caret_type" type="int" setter="set_caret_type" getter="get_caret_type" enum="TextEdit.CaretType" default="0"> + Set the type of caret to draw. + </member> <member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled" default="true"> If [code]true[/code], a right-click displays the context menu. </member> @@ -580,6 +911,9 @@ <member name="draw_tabs" type="bool" setter="set_draw_tabs" getter="is_drawing_tabs" default="false"> If [code]true[/code], the "tab" character will have a visible representation. </member> + <member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true"> + If [code]false[/code], existing text cannot be modified and new text cannot be added. + </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" /> <member name="highlight_all_occurrences" type="bool" setter="set_highlight_all_occurrences" getter="is_highlight_all_occurrences_enabled" default="false"> If [code]true[/code], all occurrences of the selected text will be highlighted. @@ -600,12 +934,18 @@ <member name="override_selected_font_color" type="bool" setter="set_override_selected_font_color" getter="is_overriding_selected_font_color" default="false"> If [code]true[/code], custom [code]font_selected_color[/code] will be used for selected text. </member> - <member name="readonly" type="bool" setter="set_readonly" getter="is_readonly" default="false"> - If [code]true[/code], read-only mode is enabled. Existing text cannot be modified and new text cannot be added. - </member> <member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll" default="0"> If there is a horizontal scrollbar, this determines the current horizontal scroll value in pixels. </member> + <member name="scroll_past_end_of_file" type="bool" setter="set_scroll_past_end_of_file_enabled" getter="is_scroll_past_end_of_file_enabled" default="false"> + Allow scrolling past the last line into "virtual" space. + </member> + <member name="scroll_smooth" type="bool" setter="set_smooth_scroll_enable" getter="is_smooth_scroll_enabled" default="false"> + Scroll smoothly over the text rather then jumping to the next location. + </member> + <member name="scroll_v_scroll_speed" type="float" setter="set_v_scroll_speed" getter="get_v_scroll_speed" default="80.0"> + Sets the scroll speed with the minimap or when [member scroll_smooth] is enabled. + </member> <member name="scroll_vertical" type="float" setter="set_v_scroll" getter="get_v_scroll" default="0.0"> If there is a vertical scrollbar, this determines the current vertical scroll value in line numbers, starting at 0 for the top line. </member> @@ -616,9 +956,6 @@ <member name="shortcut_keys_enabled" type="bool" setter="set_shortcut_keys_enabled" getter="is_shortcut_keys_enabled" default="true"> If [code]true[/code], shortcut keys for context menu items are enabled, even if the context menu is disabled. </member> - <member name="smooth_scrolling" type="bool" setter="set_smooth_scroll_enable" getter="is_smooth_scroll_enabled" default="false"> - If [code]true[/code], sets the [code]step[/code] of the scrollbars to [code]0.25[/code] which results in smoother scrolling. - </member> <member name="structured_text_bidi_override" type="int" setter="set_structured_text_bidi_override" getter="get_structured_text_bidi_override" enum="Control.StructuredTextParser" default="0"> Set BiDi algorithm override for the structured text. </member> @@ -634,40 +971,42 @@ <member name="text_direction" type="int" setter="set_text_direction" getter="get_text_direction" enum="Control.TextDirection" default="0"> Base text writing direction. </member> - <member name="v_scroll_speed" type="float" setter="set_v_scroll_speed" getter="get_v_scroll_speed" default="80.0"> - Vertical scroll sensitivity. - </member> <member name="virtual_keyboard_enabled" type="bool" setter="set_virtual_keyboard_enabled" getter="is_virtual_keyboard_enabled" default="true"> If [code]true[/code], the native virtual keyboard is shown when focused on platforms that support it. </member> - <member name="wrap_enabled" type="bool" setter="set_wrap_enabled" getter="is_wrap_enabled" default="false"> - If [code]true[/code], enables text wrapping when it goes beyond the edge of what is visible. + <member name="wrap_mode" type="int" setter="set_line_wrapping_mode" getter="get_line_wrapping_mode" enum="TextEdit.LineWrappingMode" default="0"> + Sets the line wrapping mode to use. </member> </members> <signals> - <signal name="cursor_changed"> + <signal name="caret_changed"> <description> - Emitted when the cursor changes. + Emitted when the caret changes position. </description> </signal> <signal name="gutter_added"> <description> + Emitted when a gutter is added. </description> </signal> <signal name="gutter_clicked"> <argument index="0" name="line" type="int" /> <argument index="1" name="gutter" type="int" /> <description> + Emitted when a gutter is clicked. </description> </signal> <signal name="gutter_removed"> <description> + Emitted when a gutter is removed. </description> </signal> <signal name="lines_edited_from"> <argument index="0" name="from_line" type="int" /> <argument index="1" name="to_line" type="int" /> <description> + Emitted immediately when the text changes. + When text is added [code]from_line[/code] will be less then [code]to_line[/code]. On a remove [code]to_line[/code] will be less then [code]from_line[/code]. </description> </signal> <signal name="text_changed"> @@ -677,31 +1016,6 @@ </signal> </signals> <constants> - <constant name="SEARCH_MATCH_CASE" value="1" enum="SearchFlags"> - Match case when searching. - </constant> - <constant name="SEARCH_WHOLE_WORDS" value="2" enum="SearchFlags"> - Match whole words when searching. - </constant> - <constant name="SEARCH_BACKWARDS" value="4" enum="SearchFlags"> - Search from end to beginning. - </constant> - <constant name="SELECTION_MODE_NONE" value="0" enum="SelectionMode"> - </constant> - <constant name="SELECTION_MODE_SHIFT" value="1" enum="SelectionMode"> - </constant> - <constant name="SELECTION_MODE_POINTER" value="2" enum="SelectionMode"> - </constant> - <constant name="SELECTION_MODE_WORD" value="3" enum="SelectionMode"> - </constant> - <constant name="SELECTION_MODE_LINE" value="4" enum="SelectionMode"> - </constant> - <constant name="GUTTER_TYPE_STRING" value="0" enum="GutterType"> - </constant> - <constant name="GUTTER_TYPE_ICON" value="1" enum="GutterType"> - </constant> - <constant name="GUTTER_TYPE_CUSTOM" value="2" enum="GutterType"> - </constant> <constant name="MENU_CUT" value="0" enum="MenuItems"> Cuts (copies and clears) the selected text. </constant> @@ -789,13 +1103,58 @@ <constant name="MENU_MAX" value="28" enum="MenuItems"> Represents the size of the [enum MenuItems] enum. </constant> + <constant name="SEARCH_MATCH_CASE" value="1" enum="SearchFlags"> + Match case when searching. + </constant> + <constant name="SEARCH_WHOLE_WORDS" value="2" enum="SearchFlags"> + Match whole words when searching. + </constant> + <constant name="SEARCH_BACKWARDS" value="4" enum="SearchFlags"> + Search from end to beginning. + </constant> + <constant name="CARET_TYPE_LINE" value="0" enum="CaretType"> + Vertical line caret. + </constant> + <constant name="CARET_TYPE_BLOCK" value="1" enum="CaretType"> + Block caret. + </constant> + <constant name="SELECTION_MODE_NONE" value="0" enum="SelectionMode"> + Not selecting. + </constant> + <constant name="SELECTION_MODE_SHIFT" value="1" enum="SelectionMode"> + Select as if [code]shift[/code] is pressed. + </constant> + <constant name="SELECTION_MODE_POINTER" value="2" enum="SelectionMode"> + Select single characters as if the user single clicked. + </constant> + <constant name="SELECTION_MODE_WORD" value="3" enum="SelectionMode"> + Select whole words as if the user double clicked. + </constant> + <constant name="SELECTION_MODE_LINE" value="4" enum="SelectionMode"> + Select whole lines as if the user tripped clicked. + </constant> + <constant name="LINE_WRAPPING_NONE" value="0" enum="LineWrappingMode"> + Line wrapping is disabled. + </constant> + <constant name="LINE_WRAPPING_BOUNDARY" value="1" enum="LineWrappingMode"> + Line wrapping occurs at the control boundary, beyond what would normally be visible. + </constant> + <constant name="GUTTER_TYPE_STRING" value="0" enum="GutterType"> + Draw a string. + </constant> + <constant name="GUTTER_TYPE_ICON" value="1" enum="GutterType"> + Draw an icon. + </constant> + <constant name="GUTTER_TYPE_CUSTOM" value="2" enum="GutterType"> + Custom draw. + </constant> </constants> <theme_items> <theme_item name="background_color" data_type="color" type="Color" default="Color(0, 0, 0, 0)"> Sets the background [Color] of this [TextEdit]. </theme_item> <theme_item name="caret_background_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)"> - [Color] of the text behind the caret when block caret is enabled. + [Color] of the text behind the caret when using a block caret. </theme_item> <theme_item name="caret_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)"> [Color] of the caret. @@ -816,7 +1175,7 @@ The tint of text outline of the [TextEdit]. </theme_item> <theme_item name="font_readonly_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 0.5)"> - Sets the font [Color] when [member readonly] is enabled. + Sets the font [Color] when [member editable] is disabled. </theme_item> <theme_item name="font_selected_color" data_type="color" type="Color" default="Color(0, 0, 0, 1)"> Sets the [Color] of the selected text. [member override_selected_font_color] has to be enabled. @@ -834,7 +1193,7 @@ The size of the text outline. </theme_item> <theme_item name="read_only" data_type="style" type="StyleBox"> - Sets the [StyleBox] of this [TextEdit] when [member readonly] is enabled. + Sets the [StyleBox] of this [TextEdit] when [member editable] is disabled. </theme_item> <theme_item name="selection_color" data_type="color" type="Color" default="Color(0.49, 0.49, 0.49, 1)"> Sets the highlight [Color] of text selections. diff --git a/doc/classes/VisualInstance3D.xml b/doc/classes/VisualInstance3D.xml index 2d9c266f3c..f949fbe7c0 100644 --- a/doc/classes/VisualInstance3D.xml +++ b/doc/classes/VisualInstance3D.xml @@ -27,11 +27,11 @@ Returns the RID of this instance. This RID is the same as the RID returned by [method RenderingServer.instance_create]. This RID is needed if you want to call [RenderingServer] functions directly on this [VisualInstance3D]. </description> </method> - <method name="get_layer_mask_bit" qualifiers="const"> + <method name="get_layer_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="layer" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] when the specified layer is enabled in [member layers] and [code]false[/code] otherwise. + Returns whether or not the specified layer of the [member layers] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> <method name="get_transformed_aabb" qualifiers="const"> @@ -48,12 +48,12 @@ Sets the resource that is instantiated by this [VisualInstance3D], which changes how the engine handles the [VisualInstance3D] under the hood. Equivalent to [method RenderingServer.instance_set_base]. </description> </method> - <method name="set_layer_mask_bit"> + <method name="set_layer_mask_value"> <return type="void" /> - <argument index="0" name="layer" type="int" /> - <argument index="1" name="enabled" type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> <description> - Enables a particular layer in [member layers]. + Based on [code]value[/code], enables or disables the specified layer in the [member layers], given a [code]layer_number[/code] between 1 and 20. </description> </method> </methods> diff --git a/editor/SCsub b/editor/SCsub index a976c4ed12..d149cc6273 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -30,7 +30,10 @@ if env["tools"]: reg_exporters_inc = '#include "register_exporters.h"\n' reg_exporters = "void register_exporters() {\n" for e in env.platform_exporters: - env.add_source_files(env.editor_sources, "#platform/" + e + "/export/export.cpp") + # Glob all .cpp files in export folder + files = Glob("#platform/" + e + "/export/" + "*.cpp") + env.add_source_files(env.editor_sources, files) + reg_exporters += "\tregister_" + e + "_exporter();\n" reg_exporters_inc += '#include "platform/' + e + '/export/export.h"\n' reg_exporters += "}\n" diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index eeb99b3677..4a3be1d29c 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -44,7 +44,7 @@ void GotoLineDialog::popup_find_line(CodeEdit *p_edit) { text_editor = p_edit; - line->set_text(itos(text_editor->cursor_get_line())); + line->set_text(itos(text_editor->get_caret_line())); line->select_all(); popup_centered(Size2(180, 80) * EDSCALE); line->grab_focus(); @@ -59,7 +59,7 @@ void GotoLineDialog::ok_pressed() { return; } text_editor->unfold_line(get_line() - 1); - text_editor->cursor_set_line(get_line() - 1); + text_editor->set_caret_line(get_line() - 1); hide(); } @@ -142,26 +142,23 @@ void FindReplaceBar::_unhandled_input(const Ref<InputEvent> &p_event) { } bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col) { - int line, col; String text = get_search_text(); + Point2i pos = text_editor->search(text, p_flags, p_from_line, p_from_col); - bool found = text_editor->search(text, p_flags, p_from_line, p_from_col, line, col); - - if (found) { + if (pos.x != -1) { if (!preserve_cursor && !is_selection_only()) { - text_editor->unfold_line(line); - text_editor->cursor_set_line(line, false); - text_editor->cursor_set_column(col + text.length(), false); - text_editor->center_viewport_to_cursor(); - text_editor->select(line, col, line, col + text.length()); + text_editor->unfold_line(pos.y); + text_editor->set_caret_line(pos.y, false); + text_editor->set_caret_column(pos.x + text.length(), false); + text_editor->center_viewport_to_caret(); + text_editor->select(pos.y, pos.x, pos.y, pos.x + text.length()); } text_editor->set_search_text(text); text_editor->set_search_flags(p_flags); - text_editor->set_current_search_result(line, col); - result_line = line; - result_col = col; + result_line = pos.y; + result_col = pos.x; _update_results_count(); } else { @@ -170,16 +167,15 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col) result_col = -1; text_editor->set_search_text(""); text_editor->set_search_flags(p_flags); - text_editor->set_current_search_result(line, col); } _update_matches_label(); - return found; + return pos.x != -1; } void FindReplaceBar::_replace() { - bool selection_enabled = text_editor->is_selection_active(); + bool selection_enabled = text_editor->has_selection(); Point2i selection_begin, selection_end; if (selection_enabled) { selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column()); @@ -191,8 +187,8 @@ void FindReplaceBar::_replace() { text_editor->begin_complex_operation(); if (selection_enabled && is_selection_only()) { // To restrict search_current() to selected region - text_editor->cursor_set_line(selection_begin.width); - text_editor->cursor_set_column(selection_begin.height); + text_editor->set_caret_line(selection_begin.width); + text_editor->set_caret_column(selection_begin.height); } if (search_current()) { @@ -203,13 +199,13 @@ void FindReplaceBar::_replace() { Point2i match_from(result_line, result_col); Point2i match_to(result_line, result_col + search_text_len); if (!(match_from < selection_begin || match_to > selection_end)) { - text_editor->insert_text_at_cursor(replace_text); + text_editor->insert_text_at_caret(replace_text); if (match_to.x == selection_end.x) { // Adjust selection bounds if necessary selection_end.y += replace_text.length() - search_text_len; } } } else { - text_editor->insert_text_at_cursor(replace_text); + text_editor->insert_text_at_caret(replace_text); } } text_editor->end_complex_operation(); @@ -226,10 +222,10 @@ void FindReplaceBar::_replace() { void FindReplaceBar::_replace_all() { text_editor->disconnect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed)); // Line as x so it gets priority in comparison, column as y. - Point2i orig_cursor(text_editor->cursor_get_line(), text_editor->cursor_get_column()); + Point2i orig_cursor(text_editor->get_caret_line(), text_editor->get_caret_column()); Point2i prev_match = Point2(-1, -1); - bool selection_enabled = text_editor->is_selection_active(); + bool selection_enabled = text_editor->has_selection(); Point2i selection_begin, selection_end; if (selection_enabled) { selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column()); @@ -238,8 +234,8 @@ void FindReplaceBar::_replace_all() { int vsval = text_editor->get_v_scroll(); - text_editor->cursor_set_line(0); - text_editor->cursor_set_column(0); + text_editor->set_caret_line(0); + text_editor->set_caret_column(0); String replace_text = get_replace_text(); int search_text_len = get_search_text().length(); @@ -251,8 +247,8 @@ void FindReplaceBar::_replace_all() { text_editor->begin_complex_operation(); if (selection_enabled && is_selection_only()) { - text_editor->cursor_set_line(selection_begin.width); - text_editor->cursor_set_column(selection_begin.height); + text_editor->set_caret_line(selection_begin.width); + text_editor->set_caret_column(selection_begin.height); } if (search_current()) { do { @@ -275,14 +271,14 @@ void FindReplaceBar::_replace_all() { } // Replace but adjust selection bounds. - text_editor->insert_text_at_cursor(replace_text); + text_editor->insert_text_at_caret(replace_text); if (match_to.x == selection_end.x) { selection_end.y += replace_text.length() - search_text_len; } } else { // Just replace. - text_editor->insert_text_at_cursor(replace_text); + text_editor->insert_text_at_caret(replace_text); } rc++; @@ -294,8 +290,8 @@ void FindReplaceBar::_replace_all() { replace_all_mode = false; // Restore editor state (selection, cursor, scroll). - text_editor->cursor_set_line(orig_cursor.x); - text_editor->cursor_set_column(orig_cursor.y); + text_editor->set_caret_line(orig_cursor.x); + text_editor->set_caret_column(orig_cursor.y); if (selection_enabled && is_selection_only()) { // Reselect. @@ -313,10 +309,10 @@ void FindReplaceBar::_replace_all() { } void FindReplaceBar::_get_search_from(int &r_line, int &r_col) { - r_line = text_editor->cursor_get_line(); - r_col = text_editor->cursor_get_column(); + r_line = text_editor->get_caret_line(); + r_col = text_editor->get_caret_column(); - if (text_editor->is_selection_active() && is_selection_only()) { + if (text_editor->has_selection() && is_selection_only()) { return; } @@ -409,7 +405,7 @@ bool FindReplaceBar::search_prev() { int line, col; _get_search_from(line, col); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { col--; // Skip currently selected word. } @@ -487,8 +483,8 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) { search_text->call_deferred(SNAME("grab_focus")); } - if (text_editor->is_selection_active() && !selection_only->is_pressed()) { - search_text->set_text(text_editor->get_selection_text()); + if (text_editor->has_selection() && !selection_only->is_pressed()) { + search_text->set_text(text_editor->get_selected_text()); } if (!get_search_text().is_empty()) { @@ -521,9 +517,9 @@ void FindReplaceBar::popup_replace() { hbc_option_replace->show(); } - selection_only->set_pressed((text_editor->is_selection_active() && text_editor->get_selection_from_line() < text_editor->get_selection_to_line())); + selection_only->set_pressed((text_editor->has_selection() && text_editor->get_selection_from_line() < text_editor->get_selection_to_line())); - _show_search(is_visible() || text_editor->is_selection_active()); + _show_search(is_visible() || text_editor->has_selection()); } void FindReplaceBar::_search_options_changed(bool p_pressed) { @@ -554,7 +550,7 @@ void FindReplaceBar::_search_text_submitted(const String &p_text) { } void FindReplaceBar::_replace_text_submitted(const String &p_text) { - if (selection_only->is_pressed() && text_editor->is_selection_active()) { + if (selection_only->is_pressed() && text_editor->has_selection()) { _replace_all(); _hide_bar(); } else if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { @@ -807,10 +803,10 @@ void CodeTextEditor::_reset_zoom() { } void CodeTextEditor::_line_col_changed() { - String line = text_editor->get_line(text_editor->cursor_get_line()); + String line = text_editor->get_line(text_editor->get_caret_line()); int positional_column = 0; - for (int i = 0; i < text_editor->cursor_get_column(); i++) { + for (int i = 0; i < text_editor->get_caret_column(); i++) { if (line[i] == '\t') { positional_column += text_editor->get_indent_size(); //tab size } else { @@ -820,7 +816,7 @@ void CodeTextEditor::_line_col_changed() { StringBuilder sb; sb.append("("); - sb.append(itos(text_editor->cursor_get_line() + 1).lpad(3)); + sb.append(itos(text_editor->get_caret_line() + 1).lpad(3)); sb.append(","); sb.append(itos(positional_column + 1).lpad(3)); sb.append(")"); @@ -950,11 +946,11 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_draw_bookmarks_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/show_bookmark_gutter")); text_editor->set_line_folding_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding")); text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding")); - text_editor->set_wrap_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/word_wrap")); - text_editor->set_scroll_pass_end_of_file(EditorSettings::get_singleton()->get("text_editor/cursor/scroll_past_end_of_file")); - text_editor->cursor_set_block_mode(EditorSettings::get_singleton()->get("text_editor/cursor/block_caret")); - text_editor->cursor_set_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink")); - text_editor->cursor_set_blink_speed(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink_speed")); + text_editor->set_line_wrapping_mode((TextEdit::LineWrappingMode)EditorSettings::get_singleton()->get("text_editor/appearance/word_wrap").operator int()); + text_editor->set_scroll_past_end_of_file_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/scroll_past_end_of_file")); + text_editor->set_caret_type((TextEdit::CaretType)EditorSettings::get_singleton()->get("text_editor/cursor/type").operator int()); + text_editor->set_caret_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink")); + text_editor->set_caret_blink_speed(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink_speed")); text_editor->set_auto_brace_completion_enabled(EditorSettings::get_singleton()->get("text_editor/completion/auto_brace_complete")); if (EditorSettings::get_singleton()->get("text_editor/appearance/show_line_length_guidelines")) { @@ -1039,8 +1035,8 @@ void CodeTextEditor::convert_indent_to_spaces() { indent += " "; } - int cursor_line = text_editor->cursor_get_line(); - int cursor_column = text_editor->cursor_get_column(); + int cursor_line = text_editor->get_caret_line(); + int cursor_column = text_editor->get_caret_column(); bool changed_indentation = false; for (int i = 0; i < text_editor->get_line_count(); i++) { @@ -1069,7 +1065,7 @@ void CodeTextEditor::convert_indent_to_spaces() { } } if (changed_indentation) { - text_editor->cursor_set_column(cursor_column); + text_editor->set_caret_column(cursor_column); text_editor->end_complex_operation(); text_editor->update(); } @@ -1079,8 +1075,8 @@ void CodeTextEditor::convert_indent_to_tabs() { int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); indent_size -= 1; - int cursor_line = text_editor->cursor_get_line(); - int cursor_column = text_editor->cursor_get_column(); + int cursor_line = text_editor->get_caret_line(); + int cursor_column = text_editor->get_caret_column(); bool changed_indentation = false; for (int i = 0; i < text_editor->get_line_count(); i++) { @@ -1118,14 +1114,14 @@ void CodeTextEditor::convert_indent_to_tabs() { } } if (changed_indentation) { - text_editor->cursor_set_column(cursor_column); + text_editor->set_caret_column(cursor_column); text_editor->end_complex_operation(); text_editor->update(); } } void CodeTextEditor::convert_case(CaseStyle p_case) { - if (!text_editor->is_selection_active()) { + if (!text_editor->has_selection()) { return; } @@ -1171,12 +1167,12 @@ void CodeTextEditor::convert_case(CaseStyle p_case) { void CodeTextEditor::move_lines_up() { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int from_line = text_editor->get_selection_from_line(); int from_col = text_editor->get_selection_from_column(); int to_line = text_editor->get_selection_to_line(); int to_column = text_editor->get_selection_to_column(); - int cursor_line = text_editor->cursor_get_line(); + int cursor_line = text_editor->get_caret_line(); for (int i = from_line; i <= to_line; i++) { int line_id = i; @@ -1190,15 +1186,15 @@ void CodeTextEditor::move_lines_up() { text_editor->unfold_line(next_id); text_editor->swap_lines(line_id, next_id); - text_editor->cursor_set_line(next_id); + text_editor->set_caret_line(next_id); } int from_line_up = from_line > 0 ? from_line - 1 : from_line; int to_line_up = to_line > 0 ? to_line - 1 : to_line; int cursor_line_up = cursor_line > 0 ? cursor_line - 1 : cursor_line; text_editor->select(from_line_up, from_col, to_line_up, to_column); - text_editor->cursor_set_line(cursor_line_up); + text_editor->set_caret_line(cursor_line_up); } else { - int line_id = text_editor->cursor_get_line(); + int line_id = text_editor->get_caret_line(); int next_id = line_id - 1; if (line_id == 0 || next_id < 0) { @@ -1209,7 +1205,7 @@ void CodeTextEditor::move_lines_up() { text_editor->unfold_line(next_id); text_editor->swap_lines(line_id, next_id); - text_editor->cursor_set_line(next_id); + text_editor->set_caret_line(next_id); } text_editor->end_complex_operation(); text_editor->update(); @@ -1217,12 +1213,12 @@ void CodeTextEditor::move_lines_up() { void CodeTextEditor::move_lines_down() { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int from_line = text_editor->get_selection_from_line(); int from_col = text_editor->get_selection_from_column(); int to_line = text_editor->get_selection_to_line(); int to_column = text_editor->get_selection_to_column(); - int cursor_line = text_editor->cursor_get_line(); + int cursor_line = text_editor->get_caret_line(); for (int i = to_line; i >= from_line; i--) { int line_id = i; @@ -1236,15 +1232,15 @@ void CodeTextEditor::move_lines_down() { text_editor->unfold_line(next_id); text_editor->swap_lines(line_id, next_id); - text_editor->cursor_set_line(next_id); + text_editor->set_caret_line(next_id); } int from_line_down = from_line < text_editor->get_line_count() ? from_line + 1 : from_line; int to_line_down = to_line < text_editor->get_line_count() ? to_line + 1 : to_line; int cursor_line_down = cursor_line < text_editor->get_line_count() ? cursor_line + 1 : cursor_line; text_editor->select(from_line_down, from_col, to_line_down, to_column); - text_editor->cursor_set_line(cursor_line_down); + text_editor->set_caret_line(cursor_line_down); } else { - int line_id = text_editor->cursor_get_line(); + int line_id = text_editor->get_caret_line(); int next_id = line_id + 1; if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) { @@ -1255,7 +1251,7 @@ void CodeTextEditor::move_lines_down() { text_editor->unfold_line(next_id); text_editor->swap_lines(line_id, next_id); - text_editor->cursor_set_line(next_id); + text_editor->set_caret_line(next_id); } text_editor->end_complex_operation(); text_editor->update(); @@ -1266,57 +1262,57 @@ void CodeTextEditor::_delete_line(int p_line) { // so `begin_complex_operation` is omitted here text_editor->set_line(p_line, ""); if (p_line == 0 && text_editor->get_line_count() > 1) { - text_editor->cursor_set_line(1); - text_editor->cursor_set_column(0); + text_editor->set_caret_line(1); + text_editor->set_caret_column(0); } text_editor->backspace(); text_editor->unfold_line(p_line); - text_editor->cursor_set_line(p_line); + text_editor->set_caret_line(p_line); } void CodeTextEditor::delete_lines() { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int to_line = text_editor->get_selection_to_line(); int from_line = text_editor->get_selection_from_line(); int count = Math::abs(to_line - from_line) + 1; - text_editor->cursor_set_line(from_line, false); + text_editor->set_caret_line(from_line, false); for (int i = 0; i < count; i++) { _delete_line(from_line); } text_editor->deselect(); } else { - _delete_line(text_editor->cursor_get_line()); + _delete_line(text_editor->get_caret_line()); } text_editor->end_complex_operation(); } void CodeTextEditor::duplicate_selection() { - const int cursor_column = text_editor->cursor_get_column(); - int from_line = text_editor->cursor_get_line(); - int to_line = text_editor->cursor_get_line(); + const int cursor_column = text_editor->get_caret_column(); + int from_line = text_editor->get_caret_line(); + int to_line = text_editor->get_caret_line(); int from_column = 0; int to_column = 0; int cursor_new_line = to_line + 1; - int cursor_new_column = text_editor->cursor_get_column(); + int cursor_new_column = text_editor->get_caret_column(); String new_text = "\n" + text_editor->get_line(from_line); bool selection_active = false; - text_editor->cursor_set_column(text_editor->get_line(from_line).length()); - if (text_editor->is_selection_active()) { + text_editor->set_caret_column(text_editor->get_line(from_line).length()); + if (text_editor->has_selection()) { from_column = text_editor->get_selection_from_column(); to_column = text_editor->get_selection_to_column(); from_line = text_editor->get_selection_from_line(); to_line = text_editor->get_selection_to_line(); - cursor_new_line = to_line + text_editor->cursor_get_line() - from_line; + cursor_new_line = to_line + text_editor->get_caret_line() - from_line; cursor_new_column = to_column == cursor_column ? 2 * to_column - from_column : to_column; - new_text = text_editor->get_selection_text(); + new_text = text_editor->get_selected_text(); selection_active = true; - text_editor->cursor_set_line(to_line); - text_editor->cursor_set_column(to_column); + text_editor->set_caret_line(to_line); + text_editor->set_caret_column(to_column); } text_editor->begin_complex_operation(); @@ -1325,9 +1321,9 @@ void CodeTextEditor::duplicate_selection() { text_editor->unfold_line(i); } text_editor->deselect(); - text_editor->insert_text_at_cursor(new_text); - text_editor->cursor_set_line(cursor_new_line); - text_editor->cursor_set_column(cursor_new_column); + text_editor->insert_text_at_caret(new_text); + text_editor->set_caret_line(cursor_new_line); + text_editor->set_caret_column(cursor_new_column); if (selection_active) { text_editor->select(to_line, to_column, 2 * to_line - from_line, to_line == from_line ? 2 * to_column - from_column : to_column); } @@ -1338,7 +1334,7 @@ void CodeTextEditor::duplicate_selection() { void CodeTextEditor::toggle_inline_comment(const String &delimiter) { text_editor->begin_complex_operation(); - if (text_editor->is_selection_active()) { + if (text_editor->has_selection()) { int begin = text_editor->get_selection_from_line(); int end = text_editor->get_selection_to_line(); @@ -1348,7 +1344,7 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) { } int col_to = text_editor->get_selection_to_column(); - int cursor_pos = text_editor->cursor_get_column(); + int cursor_pos = text_editor->get_caret_column(); // Check if all lines in the selected block are commented. bool is_commented = true; @@ -1377,7 +1373,7 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) { int offset = (is_commented ? -1 : 1) * delimiter.length(); int col_from = text_editor->get_selection_from_column() > 0 ? text_editor->get_selection_from_column() + offset : 0; - if (is_commented && text_editor->cursor_get_column() == text_editor->get_line(text_editor->cursor_get_line()).length() + 1) { + if (is_commented && text_editor->get_caret_column() == text_editor->get_line(text_editor->get_caret_line()).length() + 1) { cursor_pos += 1; } @@ -1385,19 +1381,19 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) { col_to += offset; } - if (text_editor->cursor_get_column() != 0) { + if (text_editor->get_caret_column() != 0) { cursor_pos += offset; } text_editor->select(begin, col_from, text_editor->get_selection_to_line(), col_to); - text_editor->cursor_set_column(cursor_pos); + text_editor->set_caret_column(cursor_pos); } else { - int begin = text_editor->cursor_get_line(); + int begin = text_editor->get_caret_line(); String line_text = text_editor->get_line(begin); int delimiter_length = delimiter.length(); - int col = text_editor->cursor_get_column(); + int col = text_editor->get_caret_column(); if (line_text.begins_with(delimiter)) { line_text = line_text.substr(delimiter_length, line_text.length()); col -= delimiter_length; @@ -1407,7 +1403,7 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) { } text_editor->set_line(begin, line_text); - text_editor->cursor_set_column(col); + text_editor->set_caret_column(col); } text_editor->end_complex_operation(); text_editor->update(); @@ -1428,7 +1424,7 @@ void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { void CodeTextEditor::goto_line_centered(int p_line) { goto_line(p_line); - text_editor->call_deferred(SNAME("center_viewport_to_cursor")); + text_editor->call_deferred(SNAME("center_viewport_to_caret")); } void CodeTextEditor::set_executing_line(int p_line) { @@ -1444,11 +1440,11 @@ Variant CodeTextEditor::get_edit_state() { state["scroll_position"] = text_editor->get_v_scroll(); state["h_scroll_position"] = text_editor->get_h_scroll(); - state["column"] = text_editor->cursor_get_column(); - state["row"] = text_editor->cursor_get_line(); + state["column"] = text_editor->get_caret_column(); + state["row"] = text_editor->get_caret_line(); - state["selection"] = get_text_editor()->is_selection_active(); - if (get_text_editor()->is_selection_active()) { + state["selection"] = get_text_editor()->has_selection(); + if (get_text_editor()->has_selection()) { state["selection_from_line"] = text_editor->get_selection_from_line(); state["selection_from_column"] = text_editor->get_selection_from_column(); state["selection_to_line"] = text_editor->get_selection_to_line(); @@ -1469,8 +1465,8 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) { Dictionary state = p_state; /* update the row first as it sets the column to 0 */ - text_editor->cursor_set_line(state["row"]); - text_editor->cursor_set_column(state["column"]); + text_editor->set_caret_line(state["row"]); + text_editor->set_caret_column(state["column"]); text_editor->set_v_scroll(state["scroll_position"]); text_editor->set_h_scroll(state["h_scroll_position"]); @@ -1517,9 +1513,9 @@ void CodeTextEditor::set_error_pos(int p_line, int p_column) { void CodeTextEditor::goto_error() { if (error->get_text() != "") { text_editor->unfold_line(error_line); - text_editor->cursor_set_line(error_line); - text_editor->cursor_set_column(error_column); - text_editor->center_viewport_to_cursor(); + text_editor->set_caret_line(error_line); + text_editor->set_caret_column(error_column); + text_editor->center_viewport_to_caret(); } } @@ -1712,7 +1708,7 @@ void CodeTextEditor::set_warning_count(int p_warning_count) { } void CodeTextEditor::toggle_bookmark() { - int line = text_editor->cursor_get_line(); + int line = text_editor->get_caret_line(); text_editor->set_line_as_bookmarked(line, !text_editor->is_line_bookmarked(line)); } @@ -1722,18 +1718,18 @@ void CodeTextEditor::goto_next_bookmark() { return; } - int line = text_editor->cursor_get_line(); + int line = text_editor->get_caret_line(); if (line >= (int)bmarks[bmarks.size() - 1]) { text_editor->unfold_line(bmarks[0]); - text_editor->cursor_set_line(bmarks[0]); - text_editor->center_viewport_to_cursor(); + text_editor->set_caret_line(bmarks[0]); + text_editor->center_viewport_to_caret(); } else { for (int i = 0; i < bmarks.size(); i++) { int bmark_line = bmarks[i]; if (bmark_line > line) { text_editor->unfold_line(bmark_line); - text_editor->cursor_set_line(bmark_line); - text_editor->center_viewport_to_cursor(); + text_editor->set_caret_line(bmark_line); + text_editor->center_viewport_to_caret(); return; } } @@ -1746,18 +1742,18 @@ void CodeTextEditor::goto_prev_bookmark() { return; } - int line = text_editor->cursor_get_line(); + int line = text_editor->get_caret_line(); if (line <= (int)bmarks[0]) { text_editor->unfold_line(bmarks[bmarks.size() - 1]); - text_editor->cursor_set_line(bmarks[bmarks.size() - 1]); - text_editor->center_viewport_to_cursor(); + text_editor->set_caret_line(bmarks[bmarks.size() - 1]); + text_editor->center_viewport_to_caret(); } else { for (int i = bmarks.size(); i >= 0; i--) { int bmark_line = bmarks[i]; if (bmark_line < line) { text_editor->unfold_line(bmark_line); - text_editor->cursor_set_line(bmark_line); - text_editor->center_viewport_to_cursor(); + text_editor->set_caret_line(bmark_line); + text_editor->center_viewport_to_caret(); return; } } @@ -1913,7 +1909,7 @@ CodeTextEditor::CodeTextEditor() { line_and_col_txt->set_mouse_filter(MOUSE_FILTER_STOP); text_editor->connect("gui_input", callable_mp(this, &CodeTextEditor::_text_editor_gui_input)); - text_editor->connect("cursor_changed", callable_mp(this, &CodeTextEditor::_line_col_changed)); + text_editor->connect("caret_changed", callable_mp(this, &CodeTextEditor::_line_col_changed)); text_editor->connect("text_changed", callable_mp(this, &CodeTextEditor::_text_changed)); text_editor->connect("request_code_completion", callable_mp(this, &CodeTextEditor::_complete_request)); TypedArray<String> cs; diff --git a/editor/editor_native_shader_source_visualizer.cpp b/editor/editor_native_shader_source_visualizer.cpp index ed2692190c..f98ac5af79 100644 --- a/editor/editor_native_shader_source_visualizer.cpp +++ b/editor/editor_native_shader_source_visualizer.cpp @@ -51,7 +51,7 @@ void EditorNativeShaderSourceVisualizer::_inspect_shader(RID p_shader) { versions->add_child(vtab); for (int j = 0; j < nsc.versions[i].stages.size(); j++) { TextEdit *vtext = memnew(TextEdit); - vtext->set_readonly(true); + vtext->set_editable(false); vtext->set_name(nsc.versions[i].stages[j].name); vtext->set_text(nsc.versions[i].stages[j].code); vtext->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 789cabea9a..99619cfc40 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -121,7 +121,7 @@ void EditorPropertyMultilineText::_open_big_text() { if (!big_text_dialog) { big_text = memnew(TextEdit); big_text->connect("text_changed", callable_mp(this, &EditorPropertyMultilineText::_big_text_changed)); - big_text->set_wrap_enabled(true); + big_text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); big_text_dialog = memnew(AcceptDialog); big_text_dialog->add_child(big_text); big_text_dialog->set_title(TTR("Edit Text:")); @@ -166,7 +166,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() { set_bottom_editor(hb); text = memnew(TextEdit); text->connect("text_changed", callable_mp(this, &EditorPropertyMultilineText::_text_changed)); - text->set_wrap_enabled(true); + text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); add_focusable(text); hb->add_child(text); text->set_h_size_flags(SIZE_EXPAND_FILL); @@ -837,7 +837,7 @@ public: Vector2 offset; offset.y = rect2.size.y * 0.75; - draw_string(font, rect2.position + offset, itos(layer_index), HALIGN_CENTER, rect2.size.x, -1, on ? text_color_on : text_color); + draw_string(font, rect2.position + offset, itos(layer_index + 1), HALIGN_CENTER, rect2.size.x, -1, on ? text_color_on : text_color); ofs.x += bsize + 1; @@ -993,12 +993,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) { for (int i = 0; i < layer_count; i++) { String name; - if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i))) { - name = ProjectSettings::get_singleton()->get(basename + vformat("/layer_%d", i)); + if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i + 1))) { + name = ProjectSettings::get_singleton()->get(basename + vformat("/layer_%d", i + 1)); } if (name == "") { - name = vformat(TTR("Layer %d"), i); + name = vformat(TTR("Layer %d"), i + 1); } names.push_back(name); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 16c2a0f028..009a83994c 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -525,7 +525,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/appearance/show_bookmark_gutter", true); _initial_set("text_editor/appearance/show_info_gutter", true); _initial_set("text_editor/appearance/code_folding", true); - _initial_set("text_editor/appearance/word_wrap", false); + _initial_set("text_editor/appearance/word_wrap", 0); + hints["text_editor/appearance/word_wrap"] = PropertyInfo(Variant::INT, "text_editor/appearance/word_wrap", PROPERTY_HINT_ENUM, "None,Boundary"); + _initial_set("text_editor/appearance/show_line_length_guidelines", true); _initial_set("text_editor/appearance/line_length_guideline_soft_column", 80); hints["text_editor/appearance/line_length_guideline_soft_column"] = PropertyInfo(Variant::INT, "text_editor/appearance/line_length_guideline_soft_column", PROPERTY_HINT_RANGE, "20, 160, 1"); @@ -546,7 +548,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Cursor _initial_set("text_editor/cursor/scroll_past_end_of_file", false); - _initial_set("text_editor/cursor/block_caret", false); + _initial_set("text_editor/cursor/type", 0); + hints["text_editor/cursor/type"] = PropertyInfo(Variant::INT, "text_editor/cursor/type", PROPERTY_HINT_ENUM, "Line,Block"); _initial_set("text_editor/cursor/caret_blink", true); _initial_set("text_editor/cursor/caret_blink_speed", 0.5); hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::FLOAT, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01"); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 5adce2951d..fe6c081922 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1048,6 +1048,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("font_readonly_color", "LineEdit", font_readonly_color); theme->set_color("caret_color", "TextEdit", font_color); theme->set_color("selection_color", "TextEdit", selection_color); + theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE); // CodeEdit theme->set_stylebox("normal", "CodeEdit", style_widget); @@ -1062,6 +1063,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("font_color", "CodeEdit", font_color); theme->set_color("caret_color", "CodeEdit", font_color); theme->set_color("selection_color", "CodeEdit", selection_color); + theme->set_constant("line_spacing", "CodeEdit", 4 * EDSCALE); // H/VSplitContainer theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon("GuiVsplitBg", "EditorIcons"), 1, 1, 1, 1)); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 5fe466140b..a3ff312497 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -282,7 +282,7 @@ PluginConfigDialog::PluginConfigDialog() { desc_edit = memnew(TextEdit); desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE); - desc_edit->set_wrap_enabled(true); + desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); grid->add_child(desc_edit); // Author diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 64381c0d9e..8cd746304c 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -241,14 +241,14 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { goto_line_centered(p_line.operator int64_t()); } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); - code_editor->get_text_editor()->insert_at("# warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); + code_editor->get_text_editor()->insert_line_at(meta["line"].operator int64_t() - 1, "# warning-ignore:" + meta["code"].operator String()); _validate_script(); } } void ScriptTextEditor::_error_clicked(Variant p_line) { if (p_line.get_type() == Variant::INT) { - code_editor->get_text_editor()->cursor_set_line(p_line.operator int64_t()); + code_editor->get_text_editor()->set_caret_line(p_line.operator int64_t()); } } @@ -256,14 +256,14 @@ void ScriptTextEditor::reload_text() { ERR_FAIL_COND(script.is_null()); CodeEdit *te = code_editor->get_text_editor(); - int column = te->cursor_get_column(); - int row = te->cursor_get_line(); + int column = te->get_caret_column(); + int row = te->get_caret_line(); int h = te->get_h_scroll(); int v = te->get_v_scroll(); te->set_text(script->get_source_code()); - te->cursor_set_line(row); - te->cursor_set_column(column); + te->set_caret_line(row); + te->set_caret_column(column); te->set_h_scroll(h); te->set_v_scroll(v); @@ -281,12 +281,12 @@ void ScriptTextEditor::add_callback(const String &p_function, PackedStringArray pos = code_editor->get_text_editor()->get_line_count() + 2; String func = script->get_language()->make_function("", p_function, p_args); //code=code+func; - code_editor->get_text_editor()->cursor_set_line(pos + 1); - code_editor->get_text_editor()->cursor_set_column(1000000); //none shall be that big - code_editor->get_text_editor()->insert_text_at_cursor("\n\n" + func); + code_editor->get_text_editor()->set_caret_line(pos + 1); + code_editor->get_text_editor()->set_caret_column(1000000); //none shall be that big + code_editor->get_text_editor()->insert_text_at_caret("\n\n" + func); } - code_editor->get_text_editor()->cursor_set_line(pos); - code_editor->get_text_editor()->cursor_set_column(1); + code_editor->get_text_editor()->set_caret_line(pos); + code_editor->get_text_editor()->set_caret_column(1); } bool ScriptTextEditor::show_members_overview() { @@ -726,7 +726,7 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) { _edit_option(breakpoints_menu->get_item_id(p_idx)); } else { code_editor->goto_line(breakpoints_menu->get_item_metadata(p_idx)); - code_editor->get_text_editor()->call_deferred(SNAME("center_viewport_to_cursor")); //Need to be deferred, because goto uses call_deferred(). + code_editor->get_text_editor()->call_deferred(SNAME("center_viewport_to_caret")); //Need to be deferred, because goto uses call_deferred(). } } @@ -884,7 +884,7 @@ void ScriptTextEditor::update_toggle_scripts_button() { void ScriptTextEditor::_update_connected_methods() { CodeEdit *text_edit = code_editor->get_text_editor(); - text_edit->set_gutter_width(connection_gutter, text_edit->get_row_height()); + text_edit->set_gutter_width(connection_gutter, text_edit->get_line_height()); for (int i = 0; i < text_edit->get_line_count(); i++) { if (text_edit->get_line_gutter_metadata(i, connection_gutter) == "") { continue; @@ -1054,7 +1054,7 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->duplicate_selection(); } break; case EDIT_TOGGLE_FOLD_LINE: { - tx->toggle_foldable_line(tx->cursor_get_line()); + tx->toggle_foldable_line(tx->get_caret_line()); tx->update(); } break; case EDIT_FOLD_ALL_LINES: { @@ -1062,7 +1062,7 @@ void ScriptTextEditor::_edit_option(int p_op) { tx->update(); } break; case EDIT_UNFOLD_ALL_LINES: { - tx->unhide_all_lines(); + tx->unfold_all_lines(); tx->update(); } break; case EDIT_TOGGLE_COMMENT: { @@ -1080,7 +1080,7 @@ void ScriptTextEditor::_edit_option(int p_op) { tx->begin_complex_operation(); int begin, end; - if (tx->is_selection_active()) { + if (tx->has_selection()) { begin = tx->get_selection_from_line(); end = tx->get_selection_to_line(); // ignore if the cursor is not past the first column @@ -1122,7 +1122,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_EVALUATE: { Expression expression; - Vector<String> lines = code_editor->get_text_editor()->get_selection_text().split("\n"); + Vector<String> lines = code_editor->get_text_editor()->get_selected_text().split("\n"); PackedStringArray results; for (int i = 0; i < lines.size(); i++) { @@ -1142,7 +1142,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } code_editor->get_text_editor()->begin_complex_operation(); //prevents creating a two-step undo - code_editor->get_text_editor()->insert_text_at_cursor(String("\n").join(results)); + code_editor->get_text_editor()->insert_text_at_caret(String("\n").join(results)); code_editor->get_text_editor()->end_complex_operation(); } break; case SEARCH_FIND: { @@ -1158,14 +1158,14 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->get_find_replace_bar()->popup_replace(); } break; case SEARCH_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); // Yep, because it doesn't make sense to instance this dialog for every single script open... // So this will be delegated to the ScriptEditor. emit_signal(SNAME("search_in_files_requested"), selected_text); } break; case REPLACE_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); emit_signal(SNAME("replace_in_files_requested"), selected_text); } break; @@ -1189,7 +1189,7 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->remove_all_bookmarks(); } break; case DEBUG_TOGGLE_BREAKPOINT: { - int line = tx->cursor_get_line(); + int line = tx->get_caret_line(); bool dobreak = !tx->is_line_breakpointed(line); tx->set_line_as_breakpoint(line, dobreak); EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak); @@ -1210,20 +1210,20 @@ void ScriptTextEditor::_edit_option(int p_op) { return; } - int line = tx->cursor_get_line(); + int line = tx->get_caret_line(); // wrap around if (line >= (int)bpoints[bpoints.size() - 1]) { tx->unfold_line(bpoints[0]); - tx->cursor_set_line(bpoints[0]); - tx->center_viewport_to_cursor(); + tx->set_caret_line(bpoints[0]); + tx->center_viewport_to_caret(); } else { for (int i = 0; i < bpoints.size(); i++) { int bline = bpoints[i]; if (bline > line) { tx->unfold_line(bline); - tx->cursor_set_line(bline); - tx->center_viewport_to_cursor(); + tx->set_caret_line(bline); + tx->center_viewport_to_caret(); return; } } @@ -1236,19 +1236,19 @@ void ScriptTextEditor::_edit_option(int p_op) { return; } - int line = tx->cursor_get_line(); + int line = tx->get_caret_line(); // wrap around if (line <= (int)bpoints[0]) { tx->unfold_line(bpoints[bpoints.size() - 1]); - tx->cursor_set_line(bpoints[bpoints.size() - 1]); - tx->center_viewport_to_cursor(); + tx->set_caret_line(bpoints[bpoints.size() - 1]); + tx->center_viewport_to_caret(); } else { for (int i = bpoints.size(); i >= 0; i--) { int bline = bpoints[i]; if (bline < line) { tx->unfold_line(bline); - tx->cursor_set_line(bline); - tx->center_viewport_to_cursor(); + tx->set_caret_line(bline); + tx->center_viewport_to_caret(); return; } } @@ -1256,21 +1256,21 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case HELP_CONTEXTUAL: { - String text = tx->get_selection_text(); + String text = tx->get_selected_text(); if (text == "") { - text = tx->get_word_under_cursor(); + text = tx->get_word_under_caret(); } if (text != "") { emit_signal(SNAME("request_help"), text); } } break; case LOOKUP_SYMBOL: { - String text = tx->get_word_under_cursor(); + String text = tx->get_word_under_caret(); if (text == "") { - text = tx->get_selection_text(); + text = tx->get_selected_text(); } if (text != "") { - _lookup_symbol(text, tx->cursor_get_line(), tx->cursor_get_column()); + _lookup_symbol(text, tx->get_caret_line(), tx->get_caret_column()); } } break; } @@ -1325,7 +1325,7 @@ void ScriptTextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { - code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_row_height()); + code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_line_height()); } break; default: break; @@ -1422,8 +1422,10 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data Dictionary d = p_data; CodeEdit *te = code_editor->get_text_editor(); - int row, col; - te->_get_mouse_pos(p_point, row, col); + + Point2i pos = te->get_line_column_at_pos(p_point); + int row = pos.y; + int col = pos.x; if (d.has("type") && String(d["type"]) == "resource") { Ref<Resource> res = d["resource"]; @@ -1436,9 +1438,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data return; } - te->cursor_set_line(row); - te->cursor_set_column(col); - te->insert_text_at_cursor(res->get_path()); + te->set_caret_line(row); + te->set_caret_column(col); + te->insert_text_at_caret(res->get_path()); } if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { @@ -1459,9 +1461,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data } } - te->cursor_set_line(row); - te->cursor_set_column(col); - te->insert_text_at_cursor(text_to_drop); + te->set_caret_line(row); + te->set_caret_column(col); + te->insert_text_at_caret(text_to_drop); } if (d.has("type") && String(d["type"]) == "nodes") { @@ -1489,9 +1491,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data text_to_drop += "\"" + path.c_escape() + "\""; } - te->cursor_set_line(row); - te->cursor_set_column(col); - te->insert_text_at_cursor(text_to_drop); + te->set_caret_line(row); + te->set_caret_column(col); + te->insert_text_at_caret(text_to_drop); } } @@ -1505,18 +1507,20 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) { local_pos = mb->get_global_position() - tx->get_global_position(); create_menu = true; - } else if (k.is_valid() && k->get_keycode() == KEY_MENU) { - local_pos = tx->_get_cursor_pixel_pos(); + } else if (k.is_valid() && k->is_action("ui_menu", true)) { + tx->adjust_viewport_to_caret(); + local_pos = tx->get_caret_draw_pos(); create_menu = true; } if (create_menu) { - int col, row; - tx->_get_mouse_pos(local_pos, row, col); + Point2i pos = tx->get_line_column_at_pos(local_pos); + int row = pos.y; + int col = pos.x; - tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); - if (tx->is_right_click_moving_caret()) { - if (tx->is_selection_active()) { + tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); + if (tx->is_move_caret_on_right_click_enabled()) { + if (tx->has_selection()) { int from_line = tx->get_selection_from_line(); int to_line = tx->get_selection_to_line(); int from_column = tx->get_selection_from_column(); @@ -1527,18 +1531,18 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { tx->deselect(); } } - if (!tx->is_selection_active()) { - tx->cursor_set_line(row, true, false); - tx->cursor_set_column(col); + if (!tx->has_selection()) { + tx->set_caret_line(row, false, false); + tx->set_caret_column(col); } } String word_at_pos = tx->get_word_at_pos(local_pos); if (word_at_pos == "") { - word_at_pos = tx->get_word_under_cursor(); + word_at_pos = tx->get_word_under_caret(); } if (word_at_pos == "") { - word_at_pos = tx->get_selection_text(); + word_at_pos = tx->get_selected_text(); } bool has_color = (word_at_pos == "Color"); @@ -1590,7 +1594,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { has_color = false; } } - _make_context_menu(tx->is_selection_active(), has_color, foldable, open_docs, goto_definition, local_pos); + _make_context_menu(tx->has_selection(), has_color, foldable, open_docs, goto_definition, local_pos); } } diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 95973f9dfd..29436e32b2 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -74,14 +74,14 @@ void ShaderTextEditor::reload_text() { ERR_FAIL_COND(shader.is_null()); CodeEdit *te = get_text_editor(); - int column = te->cursor_get_column(); - int row = te->cursor_get_line(); + int column = te->get_caret_column(); + int row = te->get_caret_line(); int h = te->get_h_scroll(); int v = te->get_v_scroll(); te->set_text(shader->get_code()); - te->cursor_set_line(row); - te->cursor_set_column(column); + te->set_caret_line(row); + te->set_caret_column(column); te->set_h_scroll(h); te->set_v_scroll(v); @@ -408,7 +408,7 @@ void ShaderEditor::_show_warnings_panel(bool p_show) { void ShaderEditor::_warning_clicked(Variant p_line) { if (p_line.get_type() == Variant::INT) { - shader_editor->get_text_editor()->cursor_set_line(p_line.operator int64_t()); + shader_editor->get_text_editor()->set_caret_line(p_line.operator int64_t()); } } @@ -550,13 +550,15 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid()) { if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) { - int col, row; CodeEdit *tx = shader_editor->get_text_editor(); - tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); - tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); - if (tx->is_right_click_moving_caret()) { - if (tx->is_selection_active()) { + Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position()); + int row = pos.y; + int col = pos.x; + tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); + + if (tx->is_move_caret_on_right_click_enabled()) { + if (tx->has_selection()) { int from_line = tx->get_selection_from_line(); int to_line = tx->get_selection_to_line(); int from_column = tx->get_selection_from_column(); @@ -567,19 +569,20 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { tx->deselect(); } } - if (!tx->is_selection_active()) { - tx->cursor_set_line(row, true, false); - tx->cursor_set_column(col); + if (!tx->has_selection()) { + tx->set_caret_line(row, true, false); + tx->set_caret_column(col); } } - _make_context_menu(tx->is_selection_active(), get_local_mouse_position()); + _make_context_menu(tx->has_selection(), get_local_mouse_position()); } } Ref<InputEventKey> k = ev; - if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_MENU) { + if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) { CodeEdit *tx = shader_editor->get_text_editor(); - _make_context_menu(tx->is_selection_active(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->_get_cursor_pixel_pos())); + tx->adjust_viewport_to_caret(); + _make_context_menu(tx->has_selection(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); context_menu->grab_focus(); } } diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index faf287b9bd..cfccf90499 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -132,14 +132,14 @@ void TextEditor::reload_text() { ERR_FAIL_COND(text_file.is_null()); CodeEdit *te = code_editor->get_text_editor(); - int column = te->cursor_get_column(); - int row = te->cursor_get_line(); + int column = te->get_caret_column(); + int row = te->get_caret_line(); int h = te->get_h_scroll(); int v = te->get_v_scroll(); te->set_text(text_file->get_text()); - te->cursor_set_line(row); - te->cursor_set_column(column); + te->set_caret_line(row); + te->set_caret_column(column); te->set_h_scroll(h); te->set_v_scroll(v); @@ -332,7 +332,7 @@ void TextEditor::_edit_option(int p_op) { code_editor->duplicate_selection(); } break; case EDIT_TOGGLE_FOLD_LINE: { - tx->toggle_foldable_line(tx->cursor_get_line()); + tx->toggle_foldable_line(tx->get_caret_line()); tx->update(); } break; case EDIT_FOLD_ALL_LINES: { @@ -340,7 +340,7 @@ void TextEditor::_edit_option(int p_op) { tx->update(); } break; case EDIT_UNFOLD_ALL_LINES: { - tx->unhide_all_lines(); + tx->unfold_all_lines(); tx->update(); } break; case EDIT_TRIM_TRAILING_WHITESAPCE: { @@ -374,14 +374,14 @@ void TextEditor::_edit_option(int p_op) { code_editor->get_find_replace_bar()->popup_replace(); } break; case SEARCH_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); // Yep, because it doesn't make sense to instance this dialog for every single script open... // So this will be delegated to the ScriptEditor. emit_signal(SNAME("search_in_files_requested"), selected_text); } break; case REPLACE_IN_FILES: { - String selected_text = code_editor->get_text_editor()->get_selection_text(); + String selected_text = code_editor->get_text_editor()->get_selected_text(); emit_signal(SNAME("replace_in_files_requested"), selected_text); } break; @@ -427,16 +427,18 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid()) { if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { - int col, row; CodeEdit *tx = code_editor->get_text_editor(); - tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); - tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); + Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position()); + int row = pos.y; + int col = pos.x; + + tx->set_move_caret_on_right_click_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); bool can_fold = tx->can_fold_line(row); bool is_folded = tx->is_line_folded(row); - if (tx->is_right_click_moving_caret()) { - if (tx->is_selection_active()) { + if (tx->is_move_caret_on_right_click_enabled()) { + if (tx->has_selection()) { int from_line = tx->get_selection_from_line(); int to_line = tx->get_selection_to_line(); int from_column = tx->get_selection_from_column(); @@ -447,23 +449,24 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { tx->deselect(); } } - if (!tx->is_selection_active()) { - tx->cursor_set_line(row, true, false); - tx->cursor_set_column(col); + if (!tx->has_selection()) { + tx->set_caret_line(row, true, false); + tx->set_caret_column(col); } } if (!mb->is_pressed()) { - _make_context_menu(tx->is_selection_active(), can_fold, is_folded, get_local_mouse_position()); + _make_context_menu(tx->has_selection(), can_fold, is_folded, get_local_mouse_position()); } } } Ref<InputEventKey> k = ev; - if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_MENU) { + if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) { CodeEdit *tx = code_editor->get_text_editor(); - int line = tx->cursor_get_line(); - _make_context_menu(tx->is_selection_active(), tx->can_fold_line(line), tx->is_line_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->_get_cursor_pixel_pos())); + int line = tx->get_caret_line(); + tx->adjust_viewport_to_caret(); + _make_context_menu(tx->has_selection(), tx->can_fold_line(line), tx->is_line_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->get_caret_draw_pos())); context_menu->grab_focus(); } } diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index a9a36427db..aaa29bcb7a 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -484,7 +484,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { commit_message->set_h_grow_direction(Control::GrowDirection::GROW_DIRECTION_BEGIN); commit_message->set_v_grow_direction(Control::GrowDirection::GROW_DIRECTION_END); commit_message->set_custom_minimum_size(Size2(200, 100)); - commit_message->set_wrap_enabled(true); + commit_message->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); commit_message->connect("text_changed", callable_mp(this, &VersionControlEditorPlugin::_update_commit_button)); commit_message->connect("gui_input", callable_mp(this, &VersionControlEditorPlugin::_commit_message_gui_input)); commit_box_vbc->add_child(commit_message); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 94be989c7e..d6ac238414 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -3978,7 +3978,7 @@ VisualShaderEditor::VisualShaderEditor() { preview_text->set_v_size_flags(Control::SIZE_EXPAND_FILL); preview_text->set_syntax_highlighter(syntax_highlighter); preview_text->set_draw_line_numbers(true); - preview_text->set_readonly(true); + preview_text->set_editable(false); error_panel = memnew(PanelContainer); preview_vbox->add_child(error_panel); diff --git a/editor/translations/af.po b/editor/translations/af.po index 18ad28af62..70e016ee65 100644 --- a/editor/translations/af.po +++ b/editor/translations/af.po @@ -353,6 +353,7 @@ msgstr "Verander Anim Herspeel" msgid "Remove Anim Track" msgstr "Verwyder Anim Baan" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Skep NUWE baan vir %s en voeg sleutel by?" @@ -377,10 +378,27 @@ msgstr "Skep" msgid "Anim Insert" msgstr "Anim Voeg In" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasie Zoem." + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Eienskappe" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Skep & Voeg by" @@ -975,7 +993,7 @@ msgstr "Skep Nuwe" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2363,6 +2381,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3157,10 +3186,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Deurlopend" @@ -3782,6 +3807,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Open 'n Lêer" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8694,6 +8728,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Hernoem AutoLaai" @@ -8724,6 +8764,12 @@ msgid "Remove All StyleBox Items" msgstr "Hernoem AutoLaai" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Gunstelinge:" @@ -12290,6 +12336,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13942,6 +13996,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ar.po b/editor/translations/ar.po index 9093792ef8..eb11aa27b6 100644 --- a/editor/translations/ar.po +++ b/editor/translations/ar.po @@ -389,6 +389,7 @@ msgstr "تغيير وضع عقدة Ø§Ù„ØØ±ÙƒØ©" msgid "Remove Anim Track" msgstr "ØØ°Ù مسار Ø§Ù„ØªØØ±ÙŠÙƒ" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "أنشئ مسار جديد Ù„ %s Ùˆ إدخال Ù…ÙØªØ§ØØŸ" @@ -413,10 +414,28 @@ msgstr "أنشئ" msgid "Anim Insert" msgstr "إدخال ØØ±ÙƒØ©" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "لا يمكن ÙØªØ '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "رسوم Ù…ØªØØ±ÙƒØ©" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "اللأعب Ø§Ù„Ù…ØªØØ±Ùƒ لا يستطيع ØªØØ±ÙŠÙƒ Ù†ÙØ³Ù‡ ,Ùقط اللاعبين الآخرين." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "لا خاصية '%s' موجودة." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "أنشي ØØ±ÙƒØ© وأدخلها" @@ -988,7 +1007,7 @@ msgstr "إنشاء %s جديد" msgid "No results for \"%s\"." msgstr "لا نتائج من أجل \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2333,6 +2352,17 @@ msgid "New Window" msgstr "Ù†Ø§ÙØ°Ø© جديدة" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "قم بالتدوير أثناء إعادة رسم Ù†Ø§ÙØ°Ø© Ø§Ù„Ù…ØØ±Ù‘ر." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "لا يمكن ØÙظ الموارد المستوردة." @@ -3187,10 +3217,6 @@ msgid "Save & Restart" msgstr "ØÙظ Ùˆ إعادة تشغيل" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "قم بالتدوير أثناء إعادة رسم Ù†Ø§ÙØ°Ø© Ø§Ù„Ù…ØØ±Ù‘ر." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "ØªØØ¯ÙŠØ« متواصل" @@ -3851,6 +3877,16 @@ msgid "Download from:" msgstr "خطأ ÙÙŠ التØÙ…يل" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "تشغيل ÙÙŠ Ø§Ù„Ù…ØªØµÙØ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "خطأ ÙÙŠ نسخ" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8708,6 +8744,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "إزالة جميع العناصر" @@ -8738,6 +8780,12 @@ msgid "Remove All StyleBox Items" msgstr "إزالة جميع العناصر" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Ø¥Ø¶Ø§ÙØ© بنود للصنÙ" @@ -12401,6 +12449,16 @@ msgstr "تعديل Ø§Ø±ØªÙØ§Ø¹ الشكل الأسطواني" msgid "Change Ray Shape Length" msgstr "تعديل طول الشكل الشعاعي" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ØØ¯Ø¯ موقع نقطة الإنØÙ†Ø§Ø¡" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ØØ¯Ø¯ موقع نقطة الإنØÙ†Ø§Ø¡" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "تغيير نص٠قطر الاسطوانة" @@ -14183,6 +14241,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "سيتم تجاهل هذا الجسم ØØªÙ‰ تضع ØªØØ¯Ø¯ له مجسمًا." @@ -15136,9 +15230,6 @@ msgstr "لا يمكن تعديل الثوابت." #~ msgid "I see..." #~ msgstr "أنا أري..." -#~ msgid "Can't open '%s'." -#~ msgstr "لا يمكن ÙØªØ '%s'." - #~ msgid "Ugh" #~ msgstr "آخخ" diff --git a/editor/translations/az.po b/editor/translations/az.po index 4ac0ae6469..6c07f98d38 100644 --- a/editor/translations/az.po +++ b/editor/translations/az.po @@ -358,6 +358,7 @@ msgstr "Animasya Döngü Rejimini DÉ™yiÅŸ" msgid "Remove Anim Track" msgstr "Animasya İzini Sil" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s üçün YENİ iz yaradılsın vÉ™ açar daxil edilsin?" @@ -383,11 +384,27 @@ msgstr "Yarat" msgid "Anim Insert" msgstr "Animasiya Daxil Et" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasiyanı TÉ™mizlÉ™mÉ™" + #: editor/animation_track_editor.cpp #, fuzzy msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer özünü canlandıra bilmÉ™z, yalnız digÉ™r playerlÉ™r." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animasiya yaradın vÉ™ É™lavÉ™ edin" @@ -975,7 +992,7 @@ msgstr "Yeni %s yarat" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2286,6 +2303,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3065,10 +3093,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3676,6 +3700,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8367,6 +8399,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8391,6 +8429,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11802,6 +11846,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13399,6 +13451,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/bg.po b/editor/translations/bg.po index b0bf9a4d6c..3045c7b781 100644 --- a/editor/translations/bg.po +++ b/editor/translations/bg.po @@ -350,6 +350,7 @@ msgstr "ПромÑна на режима на повтарÑне на анима msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Създаване на ÐОВРпътечка за %s и вмъкване на ключ?" @@ -374,10 +375,28 @@ msgstr "Създаване" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Избиране на вÑичко" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ÐнимациÑ" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "СвойÑтво" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -934,7 +953,7 @@ msgstr "Създаване на %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2244,6 +2263,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3050,10 +3080,6 @@ msgid "Save & Restart" msgstr "Запазване и реÑтартиране" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3677,6 +3703,16 @@ msgid "Download from:" msgstr "Грешка при ÑвалÑнето" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ОтварÑне във Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ Ð¼ÐµÐ½Ð¸Ð´Ð¶ÑŠÑ€" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Копиране на грешката" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8462,6 +8498,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Премахване на вÑички елементи" @@ -8492,6 +8534,12 @@ msgid "Remove All StyleBox Items" msgstr "Премахване на вÑички елементи" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "ДобавÑне на вÑички елементи" @@ -11965,6 +12013,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13637,6 +13693,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Това Ñ‚Ñло ще бъде игнорирано, докато не зададете полигонна мрежа." @@ -14016,10 +14108,6 @@ msgstr "КонÑтантите не могат да бъдат промененР#~ msgid "Select Mode (Q)" #~ msgstr "Режим на Селектиране (Q)" -#, fuzzy -#~ msgid "Snap Mode (%s)" -#~ msgstr "Избиране на вÑичко" - #~ msgid "Project List" #~ msgstr "СпиÑък Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¸" diff --git a/editor/translations/bn.po b/editor/translations/bn.po index 384ea57f6c..6cd9e3a81c 100644 --- a/editor/translations/bn.po +++ b/editor/translations/bn.po @@ -345,6 +345,7 @@ msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à§‡à¦° লà§à¦ª মোড পরিবর msgid "Remove Anim Track" msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨ (Anim) টà§à¦°à§à¦¯à¦¾à¦• রিমà§à¦ করà§à¦¨" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s à¦à¦° জনà§à¦¯ নতà§à¦¨ টà§à¦°à§à¦¯à¦¾à¦•/পথ তৈরি করতে à¦à¦¬à¦‚ চাবি পà§à¦°à¦¬à§‡à¦¶ করাতে চান?" @@ -369,10 +370,28 @@ msgstr "তৈরি করà§à¦¨" msgid "Anim Insert" msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à§‡ (Anim) অনà§à¦¤à¦°à§à¦à§à¦•à§à¦¤ করà§à¦¨" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'..' তে পরিচালনা করা সমà§à¦à¦¬ নয়" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à¦ªà§à¦²à§‡à¦¯à¦¼à¦¾à¦° নিজেই অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦Ÿ করতে পারে না, কেবল অনà§à¦¯ পà§à¦²à§‡à§Ÿà¦¾à¦°à¥¤" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "পà§à¦°à¦ªà¦¾à¦°à§à¦Ÿà¦¿:" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨ (Anim) তৈরি à¦à¦¬à¦‚ যোগ করà§à¦¨" @@ -969,7 +988,7 @@ msgstr "নতà§à¦¨ তৈরি করà§à¦¨" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2392,6 +2411,18 @@ msgid "New Window" msgstr "উইনà§à¦¡à§‹" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "à¦à¦¡à¦¿à¦Ÿà¦°à§‡à¦° পà§à¦¨-অঙà§à¦•নে à¦à¦Ÿà¦¿ ঘূরà§à¦£à¦¨ করে!" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3286,11 +3317,6 @@ msgstr "সংরকà§à¦·à¦£ à¦à¦¬à¦‚ পà§à¦¨-ইমà§à¦ªà§‹à¦°à§à¦Ÿ কঠ#: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "à¦à¦¡à¦¿à¦Ÿà¦°à§‡à¦° পà§à¦¨-অঙà§à¦•নে à¦à¦Ÿà¦¿ ঘূরà§à¦£à¦¨ করে!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "অবিচà§à¦›à¦¿à¦¨à§à¦¨/নিরবচà§à¦›à¦¿à¦¨à§à¦¨" @@ -3979,6 +4005,16 @@ msgid "Download from:" msgstr "নীচে" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "বà§à¦°à¦¾à¦‰à¦¸" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "à¦à§à¦²/সমসà§à¦¯à¦¾-সমূহ লোড করà§à¦¨" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -9179,6 +9215,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "কà§à¦²à¦¾à¦¸à§‡à¦° আইটেম অপসারণ করà§à¦¨" @@ -9209,6 +9251,12 @@ msgid "Remove All StyleBox Items" msgstr "কà§à¦²à¦¾à¦¸à§‡à¦° আইটেম অপসারণ করà§à¦¨" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "কà§à¦²à¦¾à¦¸à§‡à¦° আইটেম যোগ করà§à¦¨" @@ -13015,6 +13063,16 @@ msgstr "Capsule Shape à¦à¦° উচà§à¦šà¦¤à¦¾ পরিবরà§à¦¤à¦¨ করà msgid "Change Ray Shape Length" msgstr "Ray Shape à¦à¦° দৈরà§à¦˜à§à¦¯ পরিবরà§à¦¤à¦¨ করà§à¦¨" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "বকà§à¦°à¦°à§‡à¦–ার বিনà§à¦¦à§à¦° সà§à¦¥à¦¾à¦¨ নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "বকà§à¦°à¦°à§‡à¦–ার বিনà§à¦¦à§à¦° সà§à¦¥à¦¾à¦¨ নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14801,6 +14859,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -16018,10 +16112,6 @@ msgstr "" #~ msgid "I see..." #~ msgstr "বà§à¦à¦²à¦¾à¦®..." -#, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "'..' তে পরিচালনা করা সমà§à¦à¦¬ নয়" - #~ msgid "Ugh" #~ msgstr "আহà§â€Œ" diff --git a/editor/translations/br.po b/editor/translations/br.po index 0f61544190..adee6daaba 100644 --- a/editor/translations/br.po +++ b/editor/translations/br.po @@ -340,6 +340,7 @@ msgstr "Cheñch Mod Treiñ ar Fiñvskeudenn" msgid "Remove Anim Track" msgstr "Dilemel ar Roudenn Fiñvskeudenn" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Krouiñ ur roudenn NEVEZ evit %s ha enlakaat an alc'hwez ?" @@ -364,12 +365,28 @@ msgstr "Krouiñ" msgid "Anim Insert" msgstr "Enlakaat Fiñvskeudenn" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Tro Fiñvskeudenn" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer n'hall ket en em lakaat de fiñval, met nemet al lennerezhioù " "all." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Krouiñ & Enlakaat Fiñvskeudenn" @@ -934,7 +951,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2226,6 +2243,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3005,10 +3033,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3614,6 +3638,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8294,6 +8326,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8318,6 +8356,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11725,6 +11769,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13320,6 +13372,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ca.po b/editor/translations/ca.po index ab96816ec0..347fea679b 100644 --- a/editor/translations/ca.po +++ b/editor/translations/ca.po @@ -350,6 +350,7 @@ msgstr "Canviar Mode de bucle d'Animació" msgid "Remove Anim Track" msgstr "Treu la Pista" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Voleu crear una NOVA pista per a %s i inserir-hi una clau?" @@ -374,11 +375,29 @@ msgstr "Crea" msgid "Anim Insert" msgstr "Insereix una Animació" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "No es pot obrir '%s' ." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animació" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Un AnimationPlayer no pot animar-se a si mateix, només altres reproductors." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "No existeix cap propietat '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crea i Insereix" @@ -958,7 +977,7 @@ msgstr "Crea Nou %s" msgid "No results for \"%s\"." msgstr "No hi ha cap resultat per a «%s»." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2315,6 +2334,17 @@ msgid "New Window" msgstr "Finestra nova" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira quan la finestra de l'editor es redibuixa." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Els recursos importats no es poden desar." @@ -3190,10 +3220,6 @@ msgid "Save & Restart" msgstr "Desa i Reinicia" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira quan la finestra de l'editor es redibuixa." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualitzar contÃnuament" @@ -3868,6 +3894,16 @@ msgid "Download from:" msgstr "Error en la Baixada" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Executa-ho en el Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copia l'error" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8863,6 +8899,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Treu tots els Elements" @@ -8893,6 +8935,12 @@ msgid "Remove All StyleBox Items" msgstr "Treu tots els Elements" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Afegeix Elements de Classe" @@ -12713,6 +12761,16 @@ msgstr "Modifica l'alçada de la Forma Caixa" msgid "Change Ray Shape Length" msgstr "Modifica la longitud de la Forma Raig" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Estableix la Posició del Punt de la Corba" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Estableix la Posició del Punt de la Corba" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Canviar Radi del Cilindre" @@ -14528,6 +14586,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp #, fuzzy msgid "This body will be ignored until you set a mesh." @@ -15820,9 +15914,6 @@ msgstr "Les constants no es poden modificar." #~ msgid "I see..." #~ msgstr "Vaja..." -#~ msgid "Can't open '%s'." -#~ msgstr "No es pot obrir '%s' ." - #~ msgid "Ugh" #~ msgstr "Uf..." diff --git a/editor/translations/cs.po b/editor/translations/cs.po index 579289300b..266614bf96 100644 --- a/editor/translations/cs.po +++ b/editor/translations/cs.po @@ -362,6 +362,7 @@ msgstr "ZmÄ›nit mód smyÄky animace" msgid "Remove Anim Track" msgstr "Odstranit stopu animace" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "VytvoÅ™it NOVOU stopu pro %s a vložit klÃÄ?" @@ -386,10 +387,28 @@ msgstr "VytvoÅ™it" msgid "Anim Insert" msgstr "Animace: vložit" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Nelze otevÅ™Ãt '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animace" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nemůže animovat sám sebe, pouze ostatnà pÅ™ehrávaÄe." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Vlastnost '%s' neexistuje." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animace: VytvoÅ™it a vložit" @@ -966,7 +985,7 @@ msgstr "VytvoÅ™it nový %s" msgid "No results for \"%s\"." msgstr "Žádné výsledky pro \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2313,6 +2332,17 @@ msgid "New Window" msgstr "Nové okno" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ToÄà se, když se okno editoru pÅ™ekresluje." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Nelze uložit importované zdroje." @@ -3165,10 +3195,6 @@ msgid "Save & Restart" msgstr "Uložit a restartovat" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ToÄà se, když se okno editoru pÅ™ekresluje." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Aktualizovat průběžnÄ›" @@ -3831,6 +3857,16 @@ msgid "Download from:" msgstr "Chyba pÅ™i stahovánÃ" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Spustit v prohlÞeÄi" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "KopÃrovat chybu" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8679,6 +8715,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Odstranit vÅ¡echny položky" @@ -8709,6 +8751,12 @@ msgid "Remove All StyleBox Items" msgstr "Odstranit vÅ¡echny položky" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "PÅ™idat položky tÅ™Ãdy" @@ -12353,6 +12401,16 @@ msgstr "ZmÄ›nit výšku Cylinder Shape" msgid "Change Ray Shape Length" msgstr "ZmÄ›nit délku Ray Shape" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Nastavit pozici bodu kÅ™ivky" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Nastavit pozici bodu kÅ™ivky" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ZmÄ›nit polomÄ›r Cylinder" @@ -14097,6 +14155,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Toto tÄ›lo bude ignorováno dokud nenastavÃte sÃÅ¥." @@ -15196,9 +15290,6 @@ msgstr "Konstanty nenà možné upravovat." #~ msgid "I see..." #~ msgstr "Chápu..." -#~ msgid "Can't open '%s'." -#~ msgstr "Nelze otevÅ™Ãt '%s'." - #~ msgid "Ugh" #~ msgstr "Ups" diff --git a/editor/translations/da.po b/editor/translations/da.po index 163eb546a0..2ab69b5f05 100644 --- a/editor/translations/da.po +++ b/editor/translations/da.po @@ -363,6 +363,7 @@ msgstr "Ændre Anim Løkke" msgid "Remove Anim Track" msgstr "Fjern Anim Spor" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Opret NYT spor til %s og indsæt nøgle?" @@ -387,10 +388,28 @@ msgstr "Opret" msgid "Anim Insert" msgstr "Anim Indsæt" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan ikke Ã¥bne '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Animationsafspiller kan ikke animere sig selv, kun andre afspillere." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Egenskaber" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Opret & Indsæt" @@ -995,7 +1014,7 @@ msgstr "Opret Ny %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2393,6 +2412,18 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "Snurrer nÃ¥r editor vinduer gentegnes!" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3263,11 +3294,6 @@ msgstr "Gem & genstart" #: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "Snurrer nÃ¥r editor vinduer gentegnes!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "Kontinuerlig" @@ -3914,6 +3940,16 @@ msgid "Download from:" msgstr "Download" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ã…bn i FilhÃ¥ndtering" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Indlæs Fejl" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8912,6 +8948,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Fjern Alt" @@ -8942,6 +8984,12 @@ msgid "Remove All StyleBox Items" msgstr "Fjern Alt" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Favoritter:" @@ -12597,6 +12645,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Fjern Kurve Punktets Position" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Fjern Kurve Punktets Position" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14331,6 +14389,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -15089,9 +15183,6 @@ msgstr "Konstanter kan ikke ændres." #~ msgid "I see..." #~ msgstr "Jeg forstÃ¥r..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan ikke Ã¥bne '%s'." - #, fuzzy #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/de.po b/editor/translations/de.po index 38d565b1cd..6d57f3dcad 100644 --- a/editor/translations/de.po +++ b/editor/translations/de.po @@ -75,7 +75,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-06 06:47+0000\n" "Last-Translator: So Wieso <sowieso@dukun.de>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/" "godot/de/>\n" @@ -84,7 +84,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -406,6 +406,7 @@ msgstr "Animationswiederholungsmodus ändern" msgid "Remove Anim Track" msgstr "Spur entfernen" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "NEUE Spur für %s erstellen und Schlüsselbild hinzufügen?" @@ -430,10 +431,28 @@ msgstr "Erstellen" msgid "Anim Insert" msgstr "Einfügen" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "‚%s‘ kann nicht geöffnet werden." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer kann sich nicht selbst animieren, nur andere Objekte." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Eigenschaft ‚%s‘ existiert nicht." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animation Erstellen & Einfügen" @@ -646,9 +665,8 @@ msgid "Go to Previous Step" msgstr "Zum vorherigen Schritt" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Zurücksetzen" +msgstr "Zurücksetzen durchführen" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -667,9 +685,8 @@ msgid "Use Bezier Curves" msgstr "Bezier-Kurven nutzen" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Spuren einfügen" +msgstr "RESET Spur(en) erstellen" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -994,7 +1011,6 @@ msgid "Edit..." msgstr "Bearbeiten..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Zur Methode springen" @@ -1014,9 +1030,9 @@ msgstr "%s erstellen" msgid "No results for \"%s\"." msgstr "Keine Ergebnisse für \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Keine Beschreibung zu ‚%s‘ verfügbar." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1116,7 +1132,6 @@ msgid "Owners Of:" msgstr "Besitzer von:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " @@ -1124,11 +1139,10 @@ msgid "" msgstr "" "Ausgewählte Dateien aus dem Projekt entfernen? (Kann nicht rückgängig " "gemacht werden.)\n" -"Die Dateien können möglicherweise aus dem Papierkorb des Betriebssystems " -"wiederhergestellt werden." +"Abhängig von den Betriebssystemeinstellungen werden die Dateien in den " +"Papierkorb verschoben oder permanent gelöscht." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1139,8 +1153,8 @@ msgstr "" "Andere Ressourcen benötigen die zu entfernenden Dateien, um richtig zu " "funktionieren.\n" "Trotzdem entfernen? (Kann nicht rückgängig gemacht werden.)\n" -"Die Dateien können möglicherweise aus dem Papierkorb des Betriebssystems " -"wiederhergestellt werden." +"Abhängig von den Betriebssystemeinstellungen werden die Dateien in den " +"Papierkorb verschoben oder permanent gelöscht." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1312,41 +1326,39 @@ msgid "Licenses" msgstr "Lizenzen" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Fehler beim Öffnen der Paketdatei (kein ZIP-Format)." +msgstr "Fehler beim Öffnen der Nutzerinhaltsdatei „%s“ (kein ZIP-Format)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (existiert bereits)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Nutzerinhalt „%s“ - %d Datei(en) stehen in Konflikt mit diesem Projekt:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "Nutzerinhalt „%s“ - Kein Konflikt mit diesem Projekt:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Inhalte werden entpackt" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Die folgenden Dateien ließen sich nicht aus dem Paket extrahieren:" +msgstr "" +"Die folgenden Dateien ließen sich nicht aus dem Nutzerinhaltspaket „%s“ " +"extrahieren:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Und %s weitere Dateien." +msgstr "(und %s weitere Dateien)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Paket wurde erfolgreich installiert!" +msgstr "Nutzerinhaltspaket „%s“ wurde erfolgreich installiert!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1358,9 +1370,8 @@ msgid "Install" msgstr "Installieren" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Erweiterungenverwaltung" +msgstr "Nutzerinhalteinstallation" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1423,7 +1434,6 @@ msgid "Bypass" msgstr "Überbrückung" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Audiobusoptionen" @@ -1591,13 +1601,12 @@ msgid "Can't add autoload:" msgstr "Autoload konnte nicht hinzugefügt werden:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Datei existiert nicht." +msgstr "%s ist ein ungültiger Pfad. Datei existiert nicht." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s ist ein ungültiger Pfad. Liegt nicht im Ressourcen-Pfad (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1621,9 +1630,8 @@ msgid "Name" msgstr "Name" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Variable" +msgstr "Globale Variable" #: editor/editor_data.cpp msgid "Paste Params" @@ -1799,48 +1807,56 @@ msgstr "Importleiste" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Ermöglicht das Betrachten und Bearbeiten von 3D-Szenen." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"Ermöglicht das Bearbeiten von Skripten mithilfe des integrierten Skript-" +"Editors." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Stellt Zugriff zur Nutzerinhaltebibliothek her." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Ermöglicht das Bearbeiten der Node-Hierachie in der Szenenleiste." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Ermöglicht die Konfiguration von Signalen und Gruppen des ausgewählten Nodes " +"in der Szenenleiste." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Ermöglicht das Betrachten des lokalen Dateisystems in einer eigenen Leiste." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Ermöglicht die Konfiguration von Importeinstellungen für individuelle " +"Dateien. Benötigt die Dateisystemleiste zum Funktionieren." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Aktuell)" +msgstr "(ausgewählt)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(keins)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"Aktuell ausgewähltes Profil ‚%s‘ entfernen? Dies kann nicht rückgängig " +"gemacht werden." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1872,19 +1888,16 @@ msgid "Enable Contextual Editor" msgstr "Kontextsensitiven Editor aktivieren" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Eigenschaften:" +msgstr "Klasseneigenschaften:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "Eigenschaften und Merkmale" +msgstr "Wichtigste Funktionen:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Aktivierte Klassen:" +msgstr "Nodes und Klassen:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1903,7 +1916,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Fehler beim Speichern des Profils im Pfad: ‚%s‘." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "Auf Standardwerte zurücksetzen" @@ -1912,14 +1924,12 @@ msgid "Current Profile:" msgstr "Aktuelles Profil:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Profil löschen" +msgstr "Profil erstellen" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Kachel entfernen" +msgstr "Profil entfernen" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1939,18 +1949,18 @@ msgid "Export" msgstr "Exportieren" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Aktuelles Profil:" +msgstr "Ausgewähltes Profil bearbeiten:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Textureinstellungen" +msgstr "Zusatzoptionen:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Ein Profil erstellen oder importieren um verfügbare Klassen und " +"Einstellungen zu bearbeiten." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1977,9 +1987,8 @@ msgid "Select Current Folder" msgstr "Gegenwärtigen Ordner auswählen" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Datei existiert bereits. Überschreiben?" +msgstr "Datei existiert bereits. Soll sie überschrieben werden?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2372,6 +2381,17 @@ msgid "New Window" msgstr "Neues Fenster" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Dreht sich, wenn das Editorfenster neu gezeichnet wird." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importierte Ressourcen können nicht abgespeichert werden." @@ -2604,13 +2624,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"Die aktuelle Szene hat keine Wurzel, dennoch wurden %d bearbeitete externe " +"Ressource(n) gespeichert." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Ein Wurzel-Node wird benötigt um diese Szene zu speichern." +msgstr "" +"Ein Wurzel-Node wird benötigt um diese Szene zu speichern. Ein Wurzel-Node " +"kann in der Szenenbaumleiste hinzugefügt werden." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3004,9 +3027,8 @@ msgid "Orphan Resource Explorer..." msgstr "Verwaltung nicht verwendeter Ressourcen…" #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Projekt umbenennen" +msgstr "Aktuelles Projekt neu laden" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3167,22 +3189,20 @@ msgid "Help" msgstr "Hilfe" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Dokumentation öffnen" +msgstr "Internet-Dokumentation" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Fragen & Antworten" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Fehler berichten" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Einen Wert setzen" +msgstr "Neue Funktionalität vorschlagen" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3190,12 +3210,11 @@ msgstr "Dokumentationsvorschläge senden" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp msgid "Community" -msgstr "Community" +msgstr "Gemeinschaft" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Über" +msgstr "Über Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3247,10 +3266,6 @@ msgid "Save & Restart" msgstr "Speichern & Neu starten" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Dreht sich, wenn das Editorfenster neu gezeichnet wird." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Fortlaufend aktualisieren" @@ -3292,14 +3307,12 @@ msgid "Manage Templates" msgstr "Vorlagen verwalten" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Installiere aus Datei" +msgstr "Aus Datei installieren" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Quell-Mesh auswählen:" +msgstr "Android-Quelldateien auswählen" #: editor/editor_node.cpp msgid "" @@ -3384,7 +3397,7 @@ msgstr "Auswählen" #: editor/editor_node.cpp #, fuzzy msgid "Select Current" -msgstr "Gegenwärtigen Ordner auswählen" +msgstr "Aktuelles auswählen" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3419,9 +3432,8 @@ msgid "No sub-resources found." msgstr "Keine Unter-Ressourcen gefunden." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Keine Unter-Ressourcen gefunden." +msgstr "Liste der Unter-Ressourcen öffnen." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3448,14 +3460,12 @@ msgid "Update" msgstr "Update" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Version:" +msgstr "Version" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Autoren" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3468,14 +3478,12 @@ msgid "Measure:" msgstr "Messung:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Renderzeit (Sek)" +msgstr "Frame-Zeit (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Renderzeit ⌀ (sek)" +msgstr "Durchschnittszeit (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3502,6 +3510,13 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Inklusive: Beinhaltet Zeiten von anderen Funktionen die von dieser " +"aufgerufen wurden.\n" +"Brauchbar um Flaschenhälse zu finden.\n" +"\n" +"Eigen: Zählt nur die Zeit in dieser Funktion, nicht die Zeiten von ihr " +"aufgerufener Funktionen.\n" +"Brauchbar um einzelne Funktionen zum Optimieren zu finden." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3624,7 +3639,6 @@ msgid "Paste" msgstr "Einfügen" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Umwandeln zu %s" @@ -3674,10 +3688,9 @@ msgid "Did you forget the '_run' method?" msgstr "Hast du die '_run' Methode vergessen?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Strg-Taste halten um auf Ganzzahlen zu runden. Umschalt-Taste halten für " +"%s-Taste halten um auf Ganzzahlen zu runden. Umschalt-Taste halten für " "präzisere Änderungen." #: editor/editor_sub_scene.cpp @@ -3698,49 +3711,43 @@ msgstr "Aus Node importieren:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Den Ordner der diese Exportvorlagen enthält öffnen." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Diese Exportvorlage deinstallieren." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "Datei ‚%s‘ existiert nicht." +msgstr "Keine Mirrors verfügbar." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Mirrors werden geladen, bitte warten..." +msgstr "Mirror-Liste werden abgerufen…" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Download wird begonnen…" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Fehler beim Abrufen der URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Verbinde mit Mirror..." +msgstr "Verbindung mit Mirror wird hergestellt..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Kann Hostnamen nicht auflösen:" +msgstr "Angefragte Adresse konnte nicht aufgelöst werden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Kann nicht zu Host verbinden:" +msgstr "Verbindung zu Mirror konnte nicht hergestellt werden." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Keine Antwort von Host:" +msgstr "Keine Antwort des Mirrors." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3748,18 +3755,16 @@ msgid "Request failed." msgstr "Anfrage fehlgeschlagen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Anfrage fehlgeschlagen, zu viele Weiterleitungen" +msgstr "Anfrage in Weiterleitungsschleife gefangen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Anfrage fehlgeschlagen." +msgstr "Anfrage fehlgeschlagen:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Download abgeschlossen; Exportvorlagen werden extrahiert…" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3778,7 +3783,6 @@ msgid "Error getting the list of mirrors." msgstr "Fehler beim Laden der Spiegelserver." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" "Fehler beim Einlesen des JSON-Formats der Spiegelserverliste. Bitte diesen " @@ -3786,7 +3790,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Bester verfügbarer Mirror" #: editor/export_template_manager.cpp msgid "" @@ -3839,24 +3843,20 @@ msgid "SSL Handshake Error" msgstr "SSL-Handshake-Fehler" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Exportvorlagen-ZIP-Datei konnte nicht geöffnet werden." +msgstr "Exportvorlagendatei konnte nicht geöffnet werden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Ungültiges version.txt-Format in Templates: %s." +msgstr "Ungültiges version.txt-Format in der Exportvorlagendatei: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Keine version.txt in Templates gefunden." +msgstr "Keine version.txt in Exportvorlagendatei gefunden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Fehler bei Erzeugen des Pfads für die Vorlagen:" +msgstr "Fehler bei Erzeugen des Pfads zum Extrahieren der Exportvorlagen:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3867,9 +3867,8 @@ msgid "Importing:" msgstr "Importiere:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Template-Version ‚%s‘ entfernen?" +msgstr "Exportvorlagenversion ‚%s‘ entfernen?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3885,44 +3884,54 @@ msgstr "Aktuelle Version:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." -msgstr "" +msgstr "Exportvorlagen fehlen. Download oder Installation aus Datei nötig." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Exportvorlagen sind installiert und bereit zum Verwenden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Datei öffnen" +msgstr "Ordner öffnen" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." msgstr "" +"Order mit den installierten Exportvorlagen der aktuellen Version öffnen." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Deinstallieren" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Anfangswert für Zähler" +msgstr "Exportvorlagen der aktuellen Version deinstallieren." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Downloadfehler" +msgstr "Herunterladen von:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Im Browser ausführen" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Fehlermeldung kopieren" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Herunterladen und installieren" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Exportvorlagen der aktuellen Version vom bestmöglichen Mirror herunterladen " +"und installieren." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." @@ -3931,14 +3940,12 @@ msgstr "" "gestellt." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Installiere aus Datei" +msgstr "Aus Datei installieren" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Vorlagen aus ZIP-Datei importieren" +msgstr "Exportvorlagen aus lokaler Datei installieren." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3946,19 +3953,16 @@ msgid "Cancel" msgstr "Abbrechen" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Exportvorlagen-ZIP-Datei konnte nicht geöffnet werden." +msgstr "Herunterladen der Exportvorlagen abbrechen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Installierte Versionen:" +msgstr "Andere installierte Versionen:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Deinstallieren" +msgstr "Exportvorlage deinstallieren" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3973,6 +3977,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Download der Exportvorlage wird fortgesetzt.\n" +"Der Editor kann nach Ende des Downloads kurzfristig stocken." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4119,35 +4125,32 @@ msgid "Collapse All" msgstr "Alle einklappen" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Dateien suchen" +msgstr "Dateien sortieren" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Nach Name sortieren (aufsteigend)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Nach Name sortieren (absteigend)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Nach Typ sortieren (aufsteigend)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Nach Typ sortieren (absteigend)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Zuletzt bearbeitet" +msgstr "Nach Bearbeitungszeit sortieren (Aktuelles zuerst)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Zuletzt bearbeitet" +msgstr "Nach Bearbeitungszeit sortieren (Aktuelles zuletzt)" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4159,7 +4162,7 @@ msgstr "Umbenennen..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Suchfeld auswählen" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4471,14 +4474,12 @@ msgid "Failed to load resource." msgstr "Laden der Ressource gescheitert." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Eigenschaften" +msgstr "Eigenschaften kopieren" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Eigenschaften" +msgstr "Eigenschaften einfügen" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4503,23 +4504,20 @@ msgid "Save As..." msgstr "Speichern als..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Nicht im Ressourcen-Pfad." +msgstr "Zusatz-Ressourcenoptionen." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Ressourcen-Zwischenablage bearbeiten" +msgstr "Ressource in Zwischenablage bearbeiten" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Ressource kopieren" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Einbetten" +msgstr "Ressource zu Built-In konvertieren" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4534,9 +4532,8 @@ msgid "History of recently edited objects." msgstr "Verlauf der zuletzt bearbeiteten Objekte." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Dokumentation öffnen" +msgstr "Dokumentation zu diesem Objekt öffnen." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4547,9 +4544,8 @@ msgid "Filter properties" msgstr "Eigenschaften filtern" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Objekteigenschaften." +msgstr "Objekteigenschaften verwalten." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4795,9 +4791,8 @@ msgid "Blend:" msgstr "Blende:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Parameter geändert" +msgstr "Parameter geändert:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5527,11 +5522,11 @@ msgstr "Alle" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Vorlagen, Projekte und Demos durchsuchen" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Assets durchsuchen (ausgenommen Vorlagen, Projekte und Demos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5575,7 +5570,7 @@ msgstr "Nutzerinhalte als ZIP-Datei" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Audiovorschau abspielen/pausieren" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5836,13 +5831,13 @@ msgstr "Ankerpunkte ändern" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Spielekamera überschreiben\n" -"Überschreibt die Spielekamera mit der Kamera des Anzeigefensters des Editors." +"Projektkamera überbrücken\n" +"Überbrückt die Kamera des laufenden Projekts mit der Kamera des " +"Anzeigefensters des Editors." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5851,6 +5846,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Projektkamera überbrücken\n" +"Es wird zur Zeit keine Projektinstanz ausgeführt. Um diese Funktion zu " +"nutzen muss ein Projekt durch den Editor gestartet werden." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5918,32 +5916,27 @@ msgstr "Auswahlmodus" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Ausgewählten Node oder Übergang entfernen." +msgstr "Ziehen: Ausgewähltes Node um Pivotpunkt rotieren." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Ziehen = Verschieben" +msgstr "Alt+Ziehen = Ausgewähltes Node verschieben." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Ausgewählten Node oder Übergang entfernen." +msgstr "V: Pivotpunkt des ausgewählten Nodes festlegen." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Zeige eine Liste aller Objekte, die sich an der angeklickten Position " -"befinden\n" -"(equivalent zu Alt+RMT im Auswahlmodus)." +"Alt+RMT: Liste aller Nodes an Klickposition anzeigen, einschließlich " +"gesperrter." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "RMT: Node an Klickposition hinzufügen." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6183,14 +6176,12 @@ msgid "Clear Pose" msgstr "Pose/Stellung löschen" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Node hinzufügen" +msgstr "Node hier hinzufügen" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instanz-Szene(n)" +msgstr "Szene hier instantiieren" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6206,49 +6197,43 @@ msgstr "Sicht verschieben" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Auf 3.125% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Auf 6.25% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Auf 12.5% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Verkleinern" +msgstr "Auf 25% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Verkleinern" +msgstr "Auf 50% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Verkleinern" +msgstr "Auf 100% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Verkleinern" +msgstr "Auf 200% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Verkleinern" +msgstr "Auf 400% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Verkleinern" +msgstr "Auf 800% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Auf 1600% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6495,9 +6480,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Ein einzelnes konvexes Kollisionselement konnte nicht erzeugt werden." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Einzelne konvexe Form erstellen" +msgstr "Vereinfachte konvexe Form erstellen" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6535,9 +6519,8 @@ msgid "No mesh to debug." msgstr "Kein Mesh zu debuggen." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "Modell besitzt kein UV in dieser Schicht" +msgstr "Mesh hat kein UV in Schicht %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6602,16 +6585,19 @@ msgstr "" "Dies ist die schnellste (aber ungenauste) Methode für Kollisionsberechnungen." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Ein einzelnes konvexes Kollisionsnachbarelement erzeugen" +msgstr "Vereinfachtes konvexes Kollisionsnachbarelement erzeugen" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "" "Creates a simplified convex collision shape.\n" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Erzeugt ein vereinfachtes konvexes Collision Shape.\n" +"Dies ist ähnlich zu einem einzigen Collision Shape, kann allerdings manchmal " +"zu einfacherer Geometrie führen, auf Kosten der Genauigkeit." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" @@ -6624,8 +6610,9 @@ msgid "" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" -"Erstellt ein polygon-basiertes Kollisionselement.\n" -"Dies liegt von der Geschwindigkeit in der Mitte der beiden anderen Methoden." +"Erstellt ein polygon-basiertes Collision Shape.\n" +"Dies liegt von der Geschwindigkeit in der Mitte zwischen einer einzelnen " +"konvexen Kollision und einer polygon-basierten Kollision." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7271,24 +7258,20 @@ msgid "ResourcePreloader" msgstr "Ressourcen-Vorlader" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Horizontal umdrehen" +msgstr "Portale umdrehen" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "Anzahl generierter Punkte:" +msgstr "Generiere Punkte mittels Room" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "Anzahl generierter Punkte:" +msgstr "Generiere Punkte" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Horizontal umdrehen" +msgstr "Portal umdrehen" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7854,20 +7837,17 @@ msgid "None" msgstr "Nichts" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Status" +msgstr "Rotierung" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Translation:" +msgstr "Verschiebung" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Skalierung:" +msgstr "Skalierung" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7890,52 +7870,44 @@ msgid "Animation Key Inserted." msgstr "Animationsschlüsselbild eingefügt." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Neigen" +msgstr "Neigung:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Gierung:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Größe: " +msgstr "Größe:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Gezeichnete Objekte" +msgstr "Gezeichnete Objekte:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Materialänderungen" +msgstr "Materialänderungen:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Shader-Änderungen" +msgstr "Shader-Änderungen:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Oberflächen-Änderungen" +msgstr "Oberflächen-Änderungen:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Zeichenaufrufe" +msgstr "Zeichenaufrufe:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Eckpunkte" +msgstr "Eckpunkte:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS: %d (%s ms)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8091,9 +8063,8 @@ msgid "Freelook Slow Modifier" msgstr "Freisicht Trägheitsregler" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Ändere Kameragröße" +msgstr "Kameravorschau umschalten" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8115,9 +8086,8 @@ msgstr "" "Sie ist kein zuverlässiger Vergleichswert für die In-Spiel-Leistung." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Umwandeln zu %s" +msgstr "Räume umwandeln" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8139,7 +8109,6 @@ msgstr "" "(\"Röntgenblick\")." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Nodes am Boden einrasten" @@ -8159,7 +8128,7 @@ msgstr "Einrasten aktivieren" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Räume für Portal-Culling konvertieren." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8255,9 +8224,8 @@ msgid "View Grid" msgstr "Zeige Gitter" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Einstellungen für Ansichten" +msgstr "Portal-Culling anzeigen" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8579,221 +8547,196 @@ msgid "TextureRegion" msgstr "Texturbereich" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Farbe" +msgstr "Farben" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Schriftart" +msgstr "Schriftarten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Symbol" +msgstr "Symbole" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Style-Box" +msgstr "Style-Boxen" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} Farbe(n)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Keine Unter-Ressourcen gefunden." +msgstr "Keine Farben gefunden." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "Konstanten" +msgstr "{num} Konstante(n)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Farbkonstante." +msgstr "Keine Konstanten gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} Schriftart(en)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Nicht gefunden!" +msgstr "Keine Schriftarten gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} Symbol(e)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Nicht gefunden!" +msgstr "Keine Symbole gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} Style-Box(en)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Keine Unter-Ressourcen gefunden." +msgstr "Keine Style-Boxen gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} zurzeit ausgewählt" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Es wurde nichts zum Importieren ausgewählt." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Theme importieren" +msgstr "Am Importieren von Thema-Elementen" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Am Importieren von Elementen {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Editor verlassen?" +msgstr "Den Editor aktualisieren?" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Analysiere" +msgstr "Fertigstellen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Filter: " +msgstr "Filter:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "Mit Daten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Node auswählen" +msgstr "Nach Datentyp auswählen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Teilung zum Löschen auswählen." +msgstr "Alle sichtbaren Farbelemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Alle sichtbaren Farbelemente und deren Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "Alle sichtbaren Farbelemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren konstanten Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Alle sichtbaren konstanten Elemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "Alle sichtbaren konstanten Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Schriftart-Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Alle sichtbaren Schriftart-Elemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "Alle sichtbaren Schriftart-Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Symbolelemente auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Symbolelemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Symbolelemente abwählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Alle sichtbaren Style-Box-Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Alle sichtbaren Style-Box-Elemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "Alle sichtbaren Style-Box-Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Vorsicht: Das Hinzufügen von Symbolelement-Daten kann die Thema-Ressource " +"erheblich vergrößern." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Alle einklappen" +msgstr "Typen einklappen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Alle ausklappen" +msgstr "Typen ausklappen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Vorlagendatei auswählen" +msgstr "Alle Thema-Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Punkte auswählen" +msgstr "Mit Daten auswählen" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Alle Thema-Elemente mit Element-Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Alles auswählen" +msgstr "Alles abwählen" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "Alle Themen-Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Szene importieren" +msgstr "Ausgewähltes importieren" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8801,271 +8744,245 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"Es sind Elemente im „Elemente importieren“-Tab ausgewählt. Diese Auswahl " +"wird beim Schließen des Fensters verloren gehen.\n" +"Trotzdem schließen?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle Farbelemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Entferne Element" +msgstr "Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle konstanten Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle Schriftart-Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle Symbol-Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle StyleBox-Elemente entfernen" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Füge Klassen-Element hinzu" +msgstr "Farbelement hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Füge Klassen-Element hinzu" +msgstr "Konstantes Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Element hinzufügen" +msgstr "Schriftart-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Element hinzufügen" +msgstr "Symbol-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Alle Elemente hinzufügen" +msgstr "StyleBox-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Entferne Klassen-Element" +msgstr "Farbelement umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Entferne Klassen-Element" +msgstr "Konstantes Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Node umbenennen" +msgstr "Schriftart-Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Node umbenennen" +msgstr "Symbol-Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Ausgewähltes Element entfernen" +msgstr "StyleBox-Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "Ungültige Datei, kein Audiobus-Layout." +msgstr "Ungültige Datei, keine Thema-Ressource." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "Ungültige Datei, ist identisch mit der bearbeiteten Thema-Ressource." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Vorlagen verwalten" +msgstr "Thema-Elemente verwalten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Bearbeitbares Element" +msgstr "Element bearbeiten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Typ:" +msgstr "Typen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Typ:" +msgstr "Typ hinzufügen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Element hinzufügen" +msgstr "Element hinzufügen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Alle Elemente hinzufügen" +msgstr "StyleBox-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Entferne Element" +msgstr "Elemente entfernen:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" msgstr "Entferne Klassen-Element" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Entferne Klassen-Element" +msgstr "Eigene Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "Alle Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "GUI-Thema-Elemente" +msgstr "Thema-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Node-Name:" +msgstr "Alter Name:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Theme importieren" +msgstr "Elemente importieren" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Standard" +msgstr "Standard-Thema" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Thema bearbeiten" +msgstr "Editor-Motiv" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Ressource löschen" +msgstr "Andere Thema-Ressource auswählen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Theme importieren" +msgstr "Anderes Design" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Spur umbenennen" +msgstr "Elementumbenennung bestätigen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Stapelweise Umbenennung" +msgstr "Elementumbenennung abbrechen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Überschreibungen" +msgstr "Element überbrücken" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Diese StyleBox nicht mehr als Hauptstil markieren." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Diese StyleBox als Hauptstil markieren. Geänderte Eigenschaften dieser " +"StyleBox werden ebenfalls in allen StyleBoxen des gleichen Typs geändert." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Art" +msgstr "Typ hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Element hinzufügen" +msgstr "Elementtyp hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Node-Typ" +msgstr "Node-Typen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Standard laden" +msgstr "Standard anzeigen" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." -msgstr "" +msgstr "Standard Typelemente zusammen mit überbrückten Elementen anzeigen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Überschreibungen" +msgstr "Alle überbrücken" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Alle Standard-Typelemente überbrücken." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Designvorlagen (Thema)" +msgstr "Thema:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Exportvorlagen verwalten…" +msgstr "Elemente verwalten…" #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Themenelemente hinzufügen, entfernen, verwalten und importieren." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Vorschau" +msgstr "Vorschau hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Vorschau aktualisieren" +msgstr "Standard-Vorschau" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Quell-Mesh auswählen:" +msgstr "UI-Szene auswählen:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Auswahlmodus für Control-Nodes umschalten. Erlaubt das visuelle Auswählen " +"von Control-Nodes zum Bearbeiten." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9100,9 +9017,8 @@ msgid "Checked Radio Item" msgstr "Markiertes Element der Auswahl" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Ben. Trenner." +msgstr "Benannter Trenner" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9155,19 +9071,21 @@ msgstr "Hat,Mehrere,Einstellungen" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Ungültiger Pfad. Die PackedScene-Ressource wurde vermutlich verschoben oder " +"gelöscht." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"Ungültige PackedScene-Ressource. Muss ein Control-Node als Wurzel haben." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "Ungültige Datei, kein Audiobus-Layout." +msgstr "Ungültige Datei, keine PackedScene-Ressource." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Die Szene neu laden um ihren aktuellsten Status widerzuspiegeln." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10574,9 +10492,8 @@ msgid "VisualShader" msgstr "VisuellerShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Visuelle Eigenschaft bearbeiten" +msgstr "Visuelle Eigenschaft bearbeiten:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10701,9 +10618,8 @@ msgid "Script" msgstr "Skript" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Skript-Exportmodus:" +msgstr "GDScript-Exportmodus:" #: editor/project_export.cpp msgid "Text" @@ -10711,21 +10627,21 @@ msgstr "Text" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Kompilierter Bytecode (schnelleres Laden)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Verschlüsselt (Schlüssel unten angeben)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Ungültiger Schlüssel für Verschlüsselung (muss 64 Zeichen lang sein)" +msgstr "" +"Ungültiger Schlüssel für Verschlüsselung (muss aus 64 hexadezimalen Zeichen " +"bestehen)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Skript-Schlüssel (256 Bit hexadezimal):" +msgstr "Schlüssel zur GDScript-Verschlüsselung (256 bit, hexadezimal):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10797,7 +10713,6 @@ msgid "Imported Project" msgstr "Importiertes Projekt" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Ungültiger Projektname." @@ -11027,14 +10942,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Sollen wirklich %d Projekte gleichzeitig ausgeführt werden?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Gerät aus Liste auswählen" +msgstr "%d Projekte aus Liste entfernen?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Gerät aus Liste auswählen" +msgstr "Dieses Projekt von Liste entfernen?" #: editor/project_manager.cpp msgid "" @@ -11067,9 +10980,8 @@ msgid "Project Manager" msgstr "Projektverwaltung" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Projekte" +msgstr "Lokale Projekte" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11080,23 +10992,20 @@ msgid "Last Modified" msgstr "Zuletzt bearbeitet" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "Projekt exportieren" +msgstr "Projekt bearbeiten" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Projekt umbenennen" +msgstr "Projekt ausführen" #: editor/project_manager.cpp msgid "Scan" msgstr "Scannen" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Projekte" +msgstr "Nach Projekten suchen" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11107,14 +11016,12 @@ msgid "New Project" msgstr "Neues Projekt" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Importiertes Projekt" +msgstr "Projekt importieren" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Projekt umbenennen" +msgstr "Projekt entfernen" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11125,9 +11032,8 @@ msgid "About" msgstr "Über" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Bestandsbibliothek (AssetLib)" +msgstr "Nutzerinhalte-Projekte" #: editor/project_manager.cpp msgid "Restart Now" @@ -11139,7 +11045,7 @@ msgstr "Alles entfernen" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Ebenfalls Projektinhalte löschen (nicht rückgängig machbar!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11155,21 +11061,19 @@ msgstr "" "werden?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Eigenschaften filtern" +msgstr "Projekte filtern" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"Die Suchmaske filtert Projekte nach ihrem Namen oder der letzten Komponente " -"ihres Pfadnamens.\n" -"Um den Filter auf den gesamten Pfadnamen anzuwenden muss mindestens ein ‚/‘-" -"Zeichen in der Suchanfrage vorhanden sein." +"Diese Suchmaske filtert Projekte nach ihrem Namen oder der letzten " +"Komponente ihres Pfadnamens.\n" +"Um nach dem kompletten Pfad zu filtern muss mindestens ein ‚/‘-Zeichen in " +"der Suchanfrage vorhanden sein." #: editor/project_settings_editor.cpp msgid "Key " @@ -11177,7 +11081,7 @@ msgstr "Taste " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Physische Taste" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11225,7 +11129,7 @@ msgstr "Gerät" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (physisch)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11368,23 +11272,20 @@ msgid "Override for Feature" msgstr "Für Funktion überschreiben" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Übersetzung hinzufügen" +msgstr "%d Übersetzungen hinzufügen" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Übersetzung entfernen" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Ressourcen-Umleitung hinzufügen" +msgstr "Übersetzungsressourcenumschreibung: %d Pfad(e) hinzufügen" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Ressourcen-Umleitung hinzufügen" +msgstr "Übersetzungsressourcenumschreibung: %d Umschreibung(en) hinzufügen" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11831,12 +11732,16 @@ msgstr "Node „%s“ löschen?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Um den Zweig als Szene speichern zu können muss eine Szene im Editor " +"geöffnet sein." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Um den Zweig als Szene speichern zu können darf nur ein Node ausgewählt " +"sein. Es sind allerdings %d Nodes ausgewählt." #: editor/scene_tree_dock.cpp msgid "" @@ -11845,6 +11750,11 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Der Wurzel-Node-Zweig kann nicht als instantiierte Szene gespeichert " +"werden.\n" +"Eine bearbeitbare Kopie der aktuellen Szene kann im Kontextmenü der " +"Dateisystemleiste\n" +"oder im Menü unter „Szene > Neue geerbte Szene…“ erstellt werden." #: editor/scene_tree_dock.cpp msgid "" @@ -11852,6 +11762,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Zeig einer bereits instantiierten Szene kann nicht gespeichert werden.\n" +"Ein Abwandlung der Szene zu erstellen, kann eine geerbten Szene, basiert auf " +"der instantiierten Szene, im Menü unter „Szene > Neue geerbte Szene…“ " +"erzeugt werden." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12260,6 +12174,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Hinweis: Der Skriptname ist identisch mit dem Name eines Built-In-Typs, " +"üblicherweise ein Fehler." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12331,7 +12247,7 @@ msgstr "Fehlermeldung kopieren" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "C++-Quelldatei auf GitHub aufrufen" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12509,6 +12425,16 @@ msgstr "Zylinderformhöhe ändern" msgid "Change Ray Shape Length" msgstr "Ändere Länge der Strahlenform" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Kurvenpunktposition festlegen" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Kurvenpunktposition festlegen" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Zylinderradius ändern" @@ -12620,14 +12546,12 @@ msgid "Object can't provide a length." msgstr "Objekt kann keine Länge vorweisen." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "MeshLibrary exportieren" +msgstr "GLTF2-Mesh exportieren" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "Exportieren..." +msgstr "GLTF exportieren..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12670,9 +12594,8 @@ msgid "GridMap Paint" msgstr "GridMap zeichnen" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "GridMap-Auswahl füllen" +msgstr "GridMap-Auswahl" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12925,14 +12848,12 @@ msgid "Add Output Port" msgstr "Ausgangsschnittstelle hinzufügen" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Typ ändern" +msgstr "Schnittstellentyp ändern" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Eingangsschnittstellenname ändern" +msgstr "Schnittstellenname ändern" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13047,9 +12968,8 @@ msgid "Add Preload Node" msgstr "Preload-Node hinzufügen" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Node hinzufügen" +msgstr "Node(s) hinzufügen" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13316,37 +13236,31 @@ msgstr "Gerät aus Liste auswählen" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "Läuft auf %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "Exportiere alles" +msgstr "APK exportieren…" #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Deinstallieren" +msgstr "Am Deinstallieren…" #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Projekte werden geladen, bitte warten..." +msgstr "Am Installieren auf Gerät, bitte warten..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Konnte Szene nicht instantiieren!" +msgstr "Konnte Installation auf Gerät nicht durchführen: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "Angepasstes Skript wird ausgeführt..." +msgstr "Auf Gerät ausführen…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Ordner konnte nicht erstellt werden." +msgstr "Ließ sich nicht auf Gerät ausführen." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13475,40 +13389,37 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"‚apksigner‘ konnte nicht gefunden werden.\n" +"Ist das Programm im Android SDK build-tools-Verzeichnis vorhanden?\n" +"Das resultierende %s ist nicht signiert." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "Signiere Debug-Build %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Lese Dateien,\n" -"Bitte warten..." +msgstr "Signiere Release-Build %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Konnte Vorlage nicht zum Export öffnen:" +msgstr "Keystore konnte nicht gefunden werden, Export fehlgeschlagen." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "‚apksigner‘ gab Fehlercode #%d zurück" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "%s hinzufügen…" +msgstr "Verifiziere %s…" #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "‚apksigner‘-Verifizierung von %s fehlgeschlagen." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "Exportiere alles" +msgstr "Exportiere für Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13527,7 +13438,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Nicht unterstütztes Exportformat!\n" #: platform/android/export/export.cpp msgid "" @@ -13554,16 +13465,16 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Kann res://android/build/res/*.xml Dateien nicht mit Projektnamen " +"überschreiben" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "project.godot konnte nicht im Projektpfad gefunden werden." +msgstr "Konnte Projektdateien nicht als Gradle-Projekt exportieren\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Konnte Datei nicht schreiben:" +msgstr "Konnte Expansion-Package-Datei nicht schreiben!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13592,21 +13503,20 @@ msgstr "" "im Gradle Projektverzeichnis erscheinen." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "Animation nicht gefunden: ‚%s‘" +msgstr "Paket nicht gefunden: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Konturen erzeugen..." +msgstr "Erzeuge APK…" #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Konnte Vorlage nicht zum Export öffnen:" +msgstr "" +"Konnte keine APK-Vorlage zum Exportieren finden:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13615,16 +13525,18 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Fehlende Bibliotheken in Exportvorlage für die ausgewählten Architekturen: " +"%s.\n" +"Es muss entweder eine Exportvorlage mit den allen benötigten Bibliotheken " +"gebaut werden oder die angegebenen Architekturen müssen abgewählt werden." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "%s hinzufügen…" +msgstr "Füge Dateien hinzu…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Konnte Datei nicht schreiben:" +msgstr "Projektdateien konnten nicht exportiert werden" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13632,7 +13544,7 @@ msgstr "Richte APK aus..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Temporäres unausgerichtetes APK konnte nicht entpackt werden." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13680,45 +13592,40 @@ msgid "Could not write file:" msgstr "Konnte Datei nicht schreiben:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Konnte Datei nicht schreiben:" +msgstr "Konnte Datei nicht lesen:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "Konnte benutzerdefinierte HTML-Shell nicht lesen:" +msgstr "Konnte HTML-Shell nicht lesen:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Ordner konnte nicht erstellt werden." +msgstr "Konnte HTTP-Server-Verzeichnis nicht erstellen:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Fehler beim Speichern der Szene." +msgstr "Fehler beim Starten des HTTP-Servers:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Ungültiger Bezeichner:" +msgstr "Ungültiger Bundle-Bezeichner:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "Beglaubigung: Code-Signierung wird benötigt." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "Beglaubigung: Abgehärtete Ausführungsumgebung wird benötigt." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "Beglaubigung: Apple-ID-Name nicht angegeben." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "Beglaubigung: Apple-ID-Passwort nicht angegeben." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14162,6 +14069,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"Die Compress-Option von GIProbe gilt als veraltet wegen vorhandener Fehler " +"und hat keinen Effekt mehr.\n" +"Um diese Warnung zu deaktivieren, muss die Option deaktiviert werden." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14255,14 +14165,16 @@ msgstr "Node A und Node B müssen unterschiedliche PhysicsBody-Nodes sein" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." msgstr "" +"RoomManager darf kein direktes oder indirektes Unterelement von Portal sein." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Room darf kein direktes oder indirektes Unterelement von Portal sein." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." msgstr "" +"RoomGroup darf kein direktes oder indirektes Unterelement von Portal sein." #: scene/3d/remote_transform.cpp msgid "" @@ -14275,41 +14187,85 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." msgstr "" +"Room darf kein direktes oder indirektes Unterelement von einem anderen Room " +"sein." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager darf nicht in einem Room platziert werden." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup darf nicht in einem Room platziert werden." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"Die konvexe Hülle von Room enthält viele Ebenen.\n" +"Es bietet sich an Room-Hülle zu vereinfachen um die Leistung zu steigern." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager darf nicht in einer RoomGroup platziert werden." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList wurde nicht zugewiesen." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." msgstr "" +"Das RoomList-Node muss ein Spatial (oder ein von Spatial abgeleitetes Node) " +"sein." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Die Portal-Tiefengrenze liegt bei Null.\n" +"Nur der Raum, der die Kamera enthält, wird gerendert werden." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "Es darf nur ein RoomManager im Szenenbaum vorhanden sein." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14378,7 +14334,7 @@ msgstr "Animation nicht gefunden: ‚%s‘" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "Anim Reset anwenden" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14562,25 +14518,30 @@ msgid "Invalid comparison function for that type." msgstr "Ungültige Vergleichsfunktion für diesen Typ." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Varyings können nur in Vertex-Funktion zugewiesen werden." +msgstr "Varyings dürfen nicht in Funktion ‚%s‘ zugewiesen werden." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Varyings, welche in der ‚vertex‘-Funktion zugewiesen wurden, können nicht " +"erneut in der ‚fragment‘- oder ‚light‘-Funktion zugewiesen werden." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Varyings, welche in der ‚fragment‘-Funktion zugewiesen wurden, können nicht " +"erneut in der ‚vertex‘- oder ‚light‘-Funktion zugewiesen werden." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" msgstr "" +"Varying aus Fragment-Phase konnte nicht in gesonderter Funktion abgerufen " +"werden!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15780,9 +15741,6 @@ msgstr "Konstanten können nicht verändert werden." #~ msgid "I see..." #~ msgstr "Verstehe..." -#~ msgid "Can't open '%s'." -#~ msgstr "‚%s‘ kann nicht geöffnet werden." - #~ msgid "Ugh" #~ msgstr "Ähm" diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot index bbd0bf9a6a..0f3b125484 100644 --- a/editor/translations/editor.pot +++ b/editor/translations/editor.pot @@ -331,6 +331,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -355,10 +356,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -913,7 +929,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2204,6 +2220,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2983,10 +3010,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3591,6 +3614,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8268,6 +8299,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8292,6 +8329,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11699,6 +11742,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13293,6 +13344,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/el.po b/editor/translations/el.po index b26b28b5d4..93b5941f64 100644 --- a/editor/translations/el.po +++ b/editor/translations/el.po @@ -350,6 +350,7 @@ msgstr "Αλλαγή λειτουÏγίας επανάληψης κίνησης" msgid "Remove Anim Track" msgstr "ΑφαίÏεση ÎšÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï ÎšÎ¯Î½Î·ÏƒÎ·Ï‚" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "ΔημιουÏγία νÎου ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï Î³Î¹Î± %s και εισαγωγή κλειδιοÏ;" @@ -374,10 +375,28 @@ msgstr "ΔημιουÏγία" msgid "Anim Insert" msgstr "Εισαγωγή Κίνησης" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "ΑδÏνατο το άνοιγμα του '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Κίνηση" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Ένα AnimationPlayer δεν μποÏεί να κινήσει τον εαυτό του." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Η ιδιότητα '%s' δεν υπάÏχει." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "ΔημιουÏγία & Εισαγωγή Κίνησης" @@ -960,7 +979,7 @@ msgstr "ΔημιουÏγία νÎου %s" msgid "No results for \"%s\"." msgstr "ΚανÎνα αποτÎλεσμα για \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2312,6 +2331,17 @@ msgid "New Window" msgstr "ÎÎο ΠαÏάθυÏο" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ΠεÏιστÏÎφεται όταν το παÏάθυÏο του επεξεÏγαστή επαναχÏωματίζεται." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Οι εισαγμÎνοι πόÏοι δεν μποÏοÏν να αποθηκευτοÏν." @@ -3190,10 +3220,6 @@ msgid "Save & Restart" msgstr "Αποθήκευση & Επανεκκίνηση" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ΠεÏιστÏÎφεται όταν το παÏάθυÏο του επεξεÏγαστή επαναχÏωματίζεται." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Συνεχόμενη ΑνανÎωση" @@ -3859,6 +3885,16 @@ msgid "Download from:" msgstr "Σφάλμα λήψης" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ΕκτÎλεση στον πεÏιηγητή" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "ΑντιγÏαφή σφάλματος" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8759,6 +8795,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ΑφαίÏεση όλων των στοιχείων" @@ -8789,6 +8831,12 @@ msgid "Remove All StyleBox Items" msgstr "ΑφαίÏεση όλων των στοιχείων" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Î Ïοσθήκη στοιχείων κλάσης" @@ -12470,6 +12518,16 @@ msgstr "Αλλαγή Ύψους Σχήματος ΚυλίνδÏου" msgid "Change Ray Shape Length" msgstr "Αλλαγή μήκους ακτίνας" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ΟÏισμός θÎσης σημείου καμπÏλης" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ΟÏισμός θÎσης σημείου καμπÏλης" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Αλλαγή Ακτίνας ΚυλίνδÏου" @@ -14265,6 +14323,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Το σώμα αυτό δε θα ληφθεί υπόψιν μÎχÏι να οÏίσετε Îνα πλÎγμα (mesh)." @@ -15708,9 +15802,6 @@ msgstr "Οι σταθεÏÎÏ‚ δεν μποÏοÏν να Ï„ÏÎ¿Ï€Î¿Ï€Î¿Î¹Î·Î¸Î¿Ï #~ msgid "I see..." #~ msgstr "Εντάξει..." -#~ msgid "Can't open '%s'." -#~ msgstr "ΑδÏνατο το άνοιγμα του '%s'." - #~ msgid "Ugh" #~ msgstr "α..." diff --git a/editor/translations/eo.po b/editor/translations/eo.po index 51c69266cb..9f8c869bee 100644 --- a/editor/translations/eo.po +++ b/editor/translations/eo.po @@ -346,6 +346,7 @@ msgstr "Aliigi Animadon Iteracion Modon" msgid "Remove Anim Track" msgstr "Forigi animacian trakon" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Krei NOVAN trakon por %s kaj enmeti Ålosilon?" @@ -370,10 +371,27 @@ msgstr "Krei" msgid "Anim Insert" msgstr "Animado Enmetu" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacio" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ne povas animi si mem, nur aliajn ludantojn." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Atributo" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animado Krei & Enmeti" @@ -949,7 +967,7 @@ msgstr "Kreu novan %s" msgid "No results for \"%s\"." msgstr "Ne rezultoj por \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2299,6 +2317,17 @@ msgid "New Window" msgstr "Nova Fenestro" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Rotacius kiam la fenestron de la redaktilo redesegniÄi." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Enportitaj risurcoj ne povas konservi." @@ -3169,10 +3198,6 @@ msgid "Save & Restart" msgstr "Konservi kaj rekomenci" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Rotacius kiam la fenestron de la redaktilo redesegniÄi." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Äœisdatigi kontinue" @@ -3827,6 +3852,16 @@ msgid "Download from:" msgstr "ElÅuta eraro" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ruli en foliumilo" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopii eraro" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8655,6 +8690,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Forigi elementon" @@ -8685,6 +8726,12 @@ msgid "Remove All StyleBox Items" msgstr "Forigi elementon" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Aldoni al favoritaj" @@ -12208,6 +12255,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13839,6 +13894,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/es.po b/editor/translations/es.po index 6f0b9cd14f..eef4affde3 100644 --- a/editor/translations/es.po +++ b/editor/translations/es.po @@ -68,11 +68,12 @@ # hiking <joaquinfc@protonmail.com>, 2021. # pabloggomez <pgg2733@gmail.com>, 2021. # Erick Figueroa <querecuto@hotmail.com>, 2021. +# jonagamerpro1234 ss <js398704@gmail.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" "Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n" "Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/" "godot/es/>\n" @@ -81,7 +82,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -405,6 +406,7 @@ msgstr "Cambiar Modo Loop de Animación" msgid "Remove Anim Track" msgstr "Eliminar Pista de Animación" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "¿Crear nueva pista para %s e insertar clave?" @@ -429,10 +431,28 @@ msgstr "Crear" msgid "Anim Insert" msgstr "Insertar Animación" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "No se puede abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animación" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Un AnimationPlayer no puede animarse a sà mismo, solo a otros players." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "No existe la propiedad '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crear e Insertar Animación" @@ -645,9 +665,8 @@ msgid "Go to Previous Step" msgstr "Ir al Paso Anterior" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Resetear" +msgstr "Aplicar Restablecer" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -666,9 +685,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Pegar Pistas" +msgstr "Crear pista(s) RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -777,7 +795,7 @@ msgstr "%d coincidencias." #: editor/code_editor.cpp editor/find_in_files.cpp msgid "Match Case" -msgstr "Coincidir Mayús./Minús." +msgstr "Distinguir mayúsculas y minúsculas" #: editor/code_editor.cpp editor/find_in_files.cpp msgid "Whole Words" @@ -995,9 +1013,8 @@ msgid "Edit..." msgstr "Editar..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" -msgstr "Ir Al Método" +msgstr "Ir al Método" #: editor/create_dialog.cpp msgid "Change %s Type" @@ -1015,9 +1032,9 @@ msgstr "Crear Nuevo %s" msgid "No results for \"%s\"." msgstr "No hay resultados para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "No hay descripción disponible para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1117,18 +1134,16 @@ msgid "Owners Of:" msgstr "Propietarios De:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"¿Eliminar los archivos seleccionados del proyecto? (irreversible)\n" -"Puedes encontrar los archivos eliminados en la papelera de reciclaje del " -"sistema para restaurarlos." +"¿Eliminar los archivos seleccionados del proyecto? (No se puede deshacer).\n" +"Dependiendo de la configuración de su sistema de archivos, los archivos se " +"moverán a la papelera del sistema o se eliminarán permanentemente." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1136,11 +1151,10 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Otros recursos necesitan los archivos que estás intentando quitar para " -"funcionar.\n" -"¿Eliminarlos de todos modos? (irreversible)\n" -"Puedes encontrar los archivos eliminados en la papelera de reciclaje del " -"sistema para restaurarlos." +"Otros recursos requieren los archivos que se eliminan para que funcionen.\n" +"¿Eliminarlos de todos modos? (No se puede deshacer).\n" +"Dependiendo de la configuración de su sistema de archivos, los archivos se " +"moverán a la papelera del sistema o se eliminarán permanentemente." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1172,7 +1186,7 @@ msgstr "¡Errores al cargar!" #: editor/dependency_editor.cpp msgid "Permanently delete %d item(s)? (No undo!)" -msgstr "¿Eliminar permanentemente %d Ãtem(s)? (¡No se puede deshacer!)" +msgstr "¿Eliminar permanentemente %d elemento(s)? (¡No se puede deshacer!)" #: editor/dependency_editor.cpp msgid "Show Dependencies" @@ -1310,41 +1324,39 @@ msgid "Licenses" msgstr "Licencias" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Error al abrir el archivo del paquete (no está en formato ZIP)." +msgstr "" +"Error al abrir el archivo de assets para \"%s\" (no se encuentra en formato " +"ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Ya existe)" +msgstr "%s (ya existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "El contenido del asset \"%s\" - %d entra en conflicto con el proyecto:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Contenido del asset \"%s\" - No hay archivos en conflicto con el proyecto:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" -msgstr "Descomprimiendo Assets" +msgstr "Descomprimir Assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Los siguientes archivos no se pudieron extraer del paquete:" +msgstr "Ha fallado la extracción de los siguientes archivos del asset \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Y %d archivos más." +msgstr "(y %s archivos más)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "¡Paquete instalado con éxito!" +msgstr "¡El asset \"%s\" ha sido instalado con éxito!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1356,9 +1368,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de paquetes" +msgstr "Instalador de Assets" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1421,7 +1432,6 @@ msgid "Bypass" msgstr "Omitir" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Opciones de Bus" @@ -1589,13 +1599,12 @@ msgid "Can't add autoload:" msgstr "No se puede añadir un autoload:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." msgstr "El archivo no existe." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s es una ruta inválida. No está en la ruta del recurso (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1619,7 +1628,6 @@ msgid "Name" msgstr "Nombre" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" msgstr "Variable" @@ -1778,7 +1786,7 @@ msgstr "Editor de Script" #: editor/editor_feature_profile.cpp msgid "Asset Library" -msgstr "Biblioteca de Assets" +msgstr "LibrerÃa de Assets" #: editor/editor_feature_profile.cpp msgid "Scene Tree Editing" @@ -1798,48 +1806,54 @@ msgstr "Importación" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite ver y editar escenas 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Permite editar scripts utilizando el editor de scripts integrado." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Proporciona acceso integrado a la LibrerÃa de Assets." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permite editar la jerarquÃa de nodos en el dock de Escena." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Permite trabajar con señales y grupos del nodo seleccionado en el dock de " +"Escena." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permite navegar por el sistema de archivos local a través de un dock " +"dedicado." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Permite configurar los ajustes de importación para assets individuales. " +"Requiere el dock FileSystem para funcionar." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Actual)" +msgstr "(actual)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(ninguno)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"¿Eliminar el perfil seleccionado en este momento, '%s'? No se puede deshacer." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1871,19 +1885,16 @@ msgid "Enable Contextual Editor" msgstr "Activar el Editor Contextual" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Propiedades:" +msgstr "Propiedades de Clase:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "CaracterÃsticas" +msgstr "CaracterÃsticas principales:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Clases Activadas:" +msgstr "Clases y Nodos:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1903,7 +1914,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Error al guardar el perfil en la ruta: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "Restablecer Valores por Defecto" @@ -1912,14 +1922,12 @@ msgid "Current Profile:" msgstr "Perfil Actual:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Borrar Perfil" +msgstr "Crear perfil" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Eliminar Tile" +msgstr "Eliminar perfil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1939,18 +1947,17 @@ msgid "Export" msgstr "Exportar" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Perfil Actual:" +msgstr "Configurar el perfil seleccionado:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Opciones de textura" +msgstr "Opciones adicionales:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Crea o importa un perfil para editar las clases y propiedades disponibles." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1977,9 +1984,8 @@ msgid "Select Current Folder" msgstr "Seleccionar Carpeta Actual" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "El archivo ya existe ¿Quieres sobreescribirlo?" +msgstr "El archivo existe, ¿sobrescribirlo?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2105,11 +2111,11 @@ msgstr "Mostrar/Ocultar archivos ocultos." #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "View items as a grid of thumbnails." -msgstr "Ver Ãtems como una cuadrÃcula de miniaturas." +msgstr "Ver elementos como una cuadrÃcula de miniaturas." #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "View items as a list." -msgstr "Ver Ãtems como una lista." +msgstr "Ver elementos como una lista." #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Directories & Files:" @@ -2138,7 +2144,7 @@ msgstr "" #: editor/editor_file_system.cpp msgid "(Re)Importing Assets" -msgstr "(Re)Importando Assets" +msgstr "(Re)Importación de Assets" #: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp msgid "Top" @@ -2183,7 +2189,7 @@ msgstr "Métodos" #: editor/editor_help.cpp msgid "Theme Properties" -msgstr "Propiedades de Temas" +msgstr "Propiedades del Theme" #: editor/editor_help.cpp msgid "Enumerations" @@ -2260,7 +2266,7 @@ msgstr "Solo Propiedades" #: editor/editor_help_search.cpp msgid "Theme Properties Only" -msgstr "Solo Propiedades del Tema" +msgstr "Solo Propiedades del Theme" #: editor/editor_help_search.cpp msgid "Member Type" @@ -2288,7 +2294,7 @@ msgstr "Propiedad" #: editor/editor_help_search.cpp msgid "Theme Property" -msgstr "Propiedades del Tema" +msgstr "Propiedades del Theme" #: editor/editor_inspector.cpp editor/project_settings_editor.cpp msgid "Property:" @@ -2372,6 +2378,17 @@ msgid "New Window" msgstr "Nueva Ventana" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira cuando la ventana del editor se redibuja." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Los recursos importados no se pueden guardar." @@ -2604,13 +2621,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"La escena actual no tiene un nodo raÃz, pero %d recurso(s) externo(s) " +"modificado(s) fueron guardados igualmente." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Se necesita un nodo raÃz para guardar la escena." +msgstr "" +"Se requiere un nodo raÃz para guardar la escena. Puede agregar un nodo raÃz " +"utilizando el muelle de árbol de escenas." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2937,7 +2957,7 @@ msgstr "Convertir a..." #: editor/editor_node.cpp msgid "MeshLibrary..." -msgstr "MeshLibrary..." +msgstr "Biblioteca de malla..." #: editor/editor_node.cpp msgid "TileSet..." @@ -2999,9 +3019,8 @@ msgid "Orphan Resource Explorer..." msgstr "Explorador de Recursos Huérfanos..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Renombrar Proyecto" +msgstr "Recargar proyecto actual" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3163,22 +3182,20 @@ msgid "Help" msgstr "Ayuda" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Abrir Documentación" +msgstr "Documentación en lÃnea" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Preguntas & Respuestas" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Reportar un Bug" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Establecer valor" +msgstr "Sugerir una caracterÃstica" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3189,9 +3206,8 @@ msgid "Community" msgstr "Comunidad" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Acerca de" +msgstr "Sobre Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3243,10 +3259,6 @@ msgid "Save & Restart" msgstr "Guardar y Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira cuando la ventana del editor se redibuja." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizar Continuamente" @@ -3289,14 +3301,12 @@ msgid "Manage Templates" msgstr "Administrar Plantillas" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde archivo" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Selecciona una Malla de Origen:" +msgstr "Seleccionar el archivo fuente de Android" #: editor/editor_node.cpp msgid "" @@ -3380,9 +3390,8 @@ msgid "Select" msgstr "Seleccionar" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Seleccionar Carpeta Actual" +msgstr "Seleccionar actual" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3398,7 +3407,7 @@ msgstr "Abrir Editor de Script" #: editor/editor_node.cpp editor/project_manager.cpp msgid "Open Asset Library" -msgstr "Abrir Biblioteca de Assets" +msgstr "Abrir LibrerÃa de Assets" #: editor/editor_node.cpp msgid "Open the next Editor" @@ -3417,9 +3426,8 @@ msgid "No sub-resources found." msgstr "No se encontró ningún sub-recurso." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "No se encontró ningún sub-recurso." +msgstr "Abra una lista de sub-recursos." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3446,14 +3454,12 @@ msgid "Update" msgstr "Actualizar" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Versión:" +msgstr "Versión" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Autores" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3466,14 +3472,12 @@ msgid "Measure:" msgstr "Medida:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Duración de Fotogramas (seg)" +msgstr "Duración de Fotogramas (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Tiempo Promedio (seg)" +msgstr "Tiempo Promedio (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3500,6 +3504,12 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Inclusivo: Incluye el tiempo de otras funciones llamadas por esta función.\n" +"UtilÃzalo para detectar cuellos de botella.\n" +"\n" +"Propio: Sólo contabiliza el tiempo empleado en la propia función, no en " +"otras funciones llamadas por esa función.\n" +"UtilÃzalo para buscar funciones individuales que optimizar." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3581,7 +3591,7 @@ msgstr "Página: " #: editor/editor_properties_array_dict.cpp #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Item" -msgstr "Eliminar Ãtem" +msgstr "Eliminar Elemento" #: editor/editor_properties_array_dict.cpp msgid "New Key:" @@ -3622,7 +3632,6 @@ msgid "Paste" msgstr "Pegar" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Convertir a %s" @@ -3674,11 +3683,10 @@ msgid "Did you forget the '_run' method?" msgstr "Te olvidaste del método '_run'?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Mantén pulsado Ctrl para redondear a enteros. Mantén pulsado Shift para " -"cambios más precisos." +"Mantenga presionado %s para redondear a números enteros. Mantenga presionada " +"la tecla Mayús para cambios más precisos." #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -3698,49 +3706,43 @@ msgstr "Importar Desde Nodo:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Abra la carpeta que contiene estas plantillas." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Desinstalar estas plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "No hay ningún archivo `%s'." +msgstr "No hay espejos disponibles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Obteniendo mirrors, por favor espera..." +msgstr "Recuperar la lista de espejos..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Comenzando la descarga..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Error al solicitar la URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Conectando con Mirror...." +msgstr "Conectando con el espejo..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "No se ha podido resolver el nombre de dominio:" +msgstr "No se puede resolver la dirección solicitada." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "No se puede conectar al host:" +msgstr "No se puede conectar al espejo." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "No hay respuesta desde el host:" +msgstr "No hay respuesta del espejo." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3748,18 +3750,16 @@ msgid "Request failed." msgstr "Petición fallida." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Petición fallida, demasiadas redirecciones" +msgstr "La solicitud terminó en un bucle de redirección." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Petición fallida." +msgstr "Petición fallida:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Descarga completa; extracción de plantillas..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3775,18 +3775,17 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Error getting the list of mirrors." -msgstr "Error al obtener la lista de mirrors." +msgstr "Error al obtener la lista de espejos." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" -"Error al analizar el JSON de la lista de mirrors. ¡Por favor, informa de " -"este problema!" +"Error al analizar el JSON con la lista de espejos ¡Por favor, reporta este " +"problema!" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "El mejor espejo disponible" #: editor/export_template_manager.cpp msgid "" @@ -3839,22 +3838,18 @@ msgid "SSL Handshake Error" msgstr "Error de Negociación SSL" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "No se puede abir el zip de plantillas de exportación." +msgstr "No se puede abrir el archivo de plantillas de exportación." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." msgstr "Formato de version.txt inválido dentro de plantillas: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." msgstr "No se ha encontrado el archivo version.txt dentro de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" msgstr "Error al crear ruta para las plantillas:" @@ -3867,9 +3862,8 @@ msgid "Importing:" msgstr "Importando:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "¿Eliminar plantilla versión '%s'?" +msgstr "¿Eliminar plantilla de la versión '%s'?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3886,43 +3880,57 @@ msgstr "Versión Actual:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Faltan las plantillas de exportación. Puede descargarlas o instalarlas desde " +"un archivo." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." msgstr "" +"Las plantillas de exportación están instaladas y listas para ser utilizadas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Abrir Archivo" +msgstr "Abrir carpeta" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." msgstr "" +"Abre la carpeta que contiene las plantillas instaladas para la versión " +"actual." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Desinstalar" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Valor inicial para el contador" +msgstr "Desinstalar las plantillas de la versión actual." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Error de descarga" +msgstr "Descargar desde:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ejecutar en Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Error" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Descargar e instalar" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Descarga e instala las plantillas de la versión actual desde el mejor espejo " +"posible." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." @@ -3931,14 +3939,12 @@ msgstr "" "versiones de desarrollo." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde un archivo" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Importar plantillas desde un archivo ZIP" +msgstr "Instalar plantillas desde un archivo local." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3946,23 +3952,20 @@ msgid "Cancel" msgstr "Cancelar" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "No se puede abir el zip de plantillas de exportación." +msgstr "Cancele la descarga de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Versiones Instaladas:" +msgstr "Otras versiones instaladas:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Desinstalar" +msgstr "Desinstalar plantilla" #: editor/export_template_manager.cpp msgid "Select Template File" -msgstr "Selecciona un Archivo de Plantilla" +msgstr "Seleccionar el archivo de la plantilla" #: editor/export_template_manager.cpp msgid "Godot Export Templates" @@ -3973,6 +3976,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Las plantillas seguirán descargándose.\n" +"Es posible que experimente una breve congelación del editor cuando terminen." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4120,35 +4125,32 @@ msgid "Collapse All" msgstr "Colapsar Todo" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Buscar archivos" +msgstr "Ordenar archivos" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Ordenar por Nombre (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Ordenar por Nombre (Descendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Ordenar por Tipo (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Ordenar por Tipo (Descendente)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Última Modificación" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Primera Modificación" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4160,7 +4162,7 @@ msgstr "Renombrar..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Enfocar el cuadro de búsqueda" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4461,22 +4463,20 @@ msgstr "Cambiar el tipo de un archivo importado requiere reiniciar el editor." msgid "" "WARNING: Assets exist that use this resource, they may stop loading properly." msgstr "" -"ADVERTENCIA: Existen recursos que utilizan este recurso, pueden dejar de " -"cargar correctamente." +"ADVERTENCIA: Existen assets que utilizan este recurso, pueden dejar de " +"cargarse correctamente." #: editor/inspector_dock.cpp msgid "Failed to load resource." msgstr "Error al cargar el recurso." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Propiedades" +msgstr "Copiar Propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Propiedades" +msgstr "Pegar Propiedades" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4501,23 +4501,20 @@ msgid "Save As..." msgstr "Guardar como..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "No está en la ruta de recursos." +msgstr "Opciones de recursos extra." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Editar Portapapeles de Recursos" +msgstr "Editar Recurso desde el Portapapeles" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Copiar Recurso" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Crear Integrado" +msgstr "Crear Recursos Integrados" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4532,9 +4529,8 @@ msgid "History of recently edited objects." msgstr "Historial de objetos recientemente editados." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Abrir Documentación" +msgstr "Abrir la documentación de este objeto." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4545,9 +4541,8 @@ msgid "Filter properties" msgstr "Filtrar propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Propiedades del objeto." +msgstr "Administrar propiedades de los objetos." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4793,9 +4788,8 @@ msgid "Blend:" msgstr "Mezcla:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Parámetro Modificado" +msgstr "Parámetro Modificado:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5445,7 +5439,7 @@ msgstr "Fallo en la comprobación del hash SHA-256" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Asset Download Error:" -msgstr "Error en la descarga del asset:" +msgstr "Error de Descarga de Assets:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Downloading (%s / %s)..." @@ -5481,7 +5475,7 @@ msgstr "Error de descarga" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download for this asset is already in progress!" -msgstr "¡Éste asset ya está descargándose!" +msgstr "¡La descarga de este asset ya está en proceso!" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Recently Updated" @@ -5529,11 +5523,11 @@ msgstr "Todos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Buscar plantillas, proyectos y demostraciones" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Buscar assets (excluyendo plantillas, proyectos y demos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5573,11 +5567,11 @@ msgstr "Cargar..." #: editor/plugins/asset_library_editor_plugin.cpp msgid "Assets ZIP File" -msgstr "Archivo ZIP de elementos" +msgstr "Archivo ZIP de assets" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Previsualización de Audio Reproducir/Pausar" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5838,13 +5832,13 @@ msgstr "Cambiar Anclas" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Reemplazar Cámara del Juego\n" -"Reemplaza la cámara del juego por la cámara del viewport del editor." +"Anulación de la Cámara del Proyecto\n" +"Anula la cámara del proyecto en ejecución por la cámara del viewport del " +"editor." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5853,6 +5847,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Anulación de la Cámara del Proyecto\n" +"No se está ejecutando ninguna instancia del proyecto. Ejecuta el proyecto " +"desde el editor para utilizar esta función." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5920,32 +5917,27 @@ msgstr "Modo de Selección" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Eliminar el nodo o transición seleccionado/a." +msgstr "Arrastrar: Girar el nodo seleccionado alrededor del pivote." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Arrastrar: Mover" +msgstr "Alt+Arrastrar: Mover el nodo seleccionado." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Eliminar el nodo o transición seleccionado/a." +msgstr "V: Establecer la posición de pivote del nodo seleccionado." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Mostrar una lista de todos los objetos en la posición en la que se ha hecho " -"clic\n" -"(igual que Alt + Clic Derecho en modo selección)." +"Alt + RMB: Muestra la lista de todos los nodos en la posición en la que se " +"hizo clic, incluido el bloqueado." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "RMB: Añade un nodo en la posición seleccionada." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6184,14 +6176,12 @@ msgid "Clear Pose" msgstr "Limpiar Pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Añadir Nodo" +msgstr "Añadir Nodo AquÃ" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instanciar Escena(s)" +msgstr "Instanciar Escena AquÃ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6207,49 +6197,43 @@ msgstr "Vista Panorámica" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Zoom al 3,125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Zoom al 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Zoom al 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Alejar Zoom" +msgstr "Zoom al 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Alejar Zoom" +msgstr "Zoom al 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Alejar Zoom" +msgstr "Zoom al 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Alejar Zoom" +msgstr "Zoom al 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Alejar Zoom" +msgstr "Zoom al 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Alejar Zoom" +msgstr "Zoom al 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Zoom al 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6451,15 +6435,15 @@ msgstr "Degradado Editado" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item %d" -msgstr "Ãtem %d" +msgstr "Elemento %d" #: editor/plugins/item_list_editor_plugin.cpp msgid "Items" -msgstr "Ãtems" +msgstr "Elementos" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item List Editor" -msgstr "Editor de Lista de Ãtems" +msgstr "Editor de Lista de Elementos" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Create Occluder Polygon" @@ -6496,9 +6480,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "No pudo crear una única forma de colisión convexa." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Crear una Única Forma Convexa" +msgstr "Crear Forma Convexa Simplificada" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6535,9 +6518,8 @@ msgid "No mesh to debug." msgstr "No hay mallas para depurar." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "El modelo no tiene UV en esta capa" +msgstr "La malla no tiene UV en la capa %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6602,9 +6584,8 @@ msgstr "" "Es la opción más rápida (pero menos precisa) para la detección de colisiones." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Crear Collider Convexo Único Hermano" +msgstr "Crear Forma de Colisión Conexa Hermana" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6612,20 +6593,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Crea una forma de colisión convexa simplificada.\n" +"Esto es similar a la forma de colisión simple, pero puede resultar en una " +"geometrÃa más simple en algunos casos, a costa de la precisión." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Crear Múltiples Collider Convexos Hermanos" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "Crea una forma de colisión basada en polÃgonos.\n" -"Este es un punto medio de rendimiento entre las dos opciones anteriores." +"Se trata de un punto intermedio de rendimiento entre una colisión convexa " +"única y una colisión basada en polÃgonos." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -6669,7 +6653,7 @@ msgstr "Depuración del Canal UV" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Remove item %d?" -msgstr "¿Eliminar el Ãtem %d?" +msgstr "¿Eliminar el elemento %d?" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "" @@ -6685,11 +6669,11 @@ msgstr "LibrerÃa de Mallas" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Add Item" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Remove Selected Item" -msgstr "Eliminar Ãtem Seleccionado" +msgstr "Eliminar Elemento Seleccionado" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Import from Scene" @@ -6742,7 +6726,7 @@ msgstr "El origen de la superficie no es correcto (sin caras)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Source Mesh:" -msgstr "Selecciona una Malla de Origen:" +msgstr "Seleccionar una Malla de Origen:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Target Surface:" @@ -7275,24 +7259,20 @@ msgid "ResourcePreloader" msgstr "Precargador de Recursos" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Voltear Horizontalmente" +msgstr "Voltear Portales" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "Conteo de Puntos Generados:" +msgstr "Generar Puntos en la Room" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "Conteo de Puntos Generados:" +msgstr "Generar puntos" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Voltear Horizontalmente" +msgstr "Voltear Portal" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7324,7 +7304,7 @@ msgstr "¡Error guardando archivo!" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme." -msgstr "Error al guardar el tema." +msgstr "Error al guardar el theme." #: editor/plugins/script_editor_plugin.cpp msgid "Error Saving" @@ -7332,7 +7312,7 @@ msgstr "Error al Guardar" #: editor/plugins/script_editor_plugin.cpp msgid "Error importing theme." -msgstr "Error al importar el tema." +msgstr "Error al importar el theme." #: editor/plugins/script_editor_plugin.cpp msgid "Error Importing" @@ -7371,11 +7351,11 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp msgid "Import Theme" -msgstr "Importar Tema" +msgstr "Importar Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme" -msgstr "Error al guardar el tema" +msgstr "Error al guardar el theme" #: editor/plugins/script_editor_plugin.cpp msgid "Error saving" @@ -7383,7 +7363,7 @@ msgstr "Error al guardar" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme As..." -msgstr "Guardar Tema Como..." +msgstr "Guardar Theme Como..." #: editor/plugins/script_editor_plugin.cpp msgid "%s Class Reference" @@ -7470,19 +7450,19 @@ msgstr "Siguiente en el Historial" #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/theme_editor_plugin.cpp msgid "Theme" -msgstr "Tema" +msgstr "Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Import Theme..." -msgstr "Importar Tema..." +msgstr "Importar Theme..." #: editor/plugins/script_editor_plugin.cpp msgid "Reload Theme" -msgstr "Recargar Tema" +msgstr "Recargar Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme" -msgstr "Guardar Tema" +msgstr "Guardar Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Close All" @@ -7855,20 +7835,17 @@ msgid "None" msgstr "Ninguno" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Estado:" +msgstr "Rotar" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Trasladar:" +msgstr "Mover" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Escala:" +msgstr "Escala" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7891,52 +7868,44 @@ msgid "Animation Key Inserted." msgstr "Clave de animación insertada." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Altura" +msgstr "Eje de paso:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Eje de guiñada:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Tamaño: " +msgstr "Tamaño:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Objetos Dibujados" +msgstr "Objetos Dibujados:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Cambios del Material" +msgstr "Cambios del Material:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Cambios del Shader" +msgstr "Cambios de sombreado:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Cambios de Superficie" +msgstr "Cambios de superficie:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Llamadas de Dibujado" +msgstr "Llamadas de Dibujado:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Vértices" +msgstr "Vértices:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS: %d (%s ms)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8091,9 +8060,8 @@ msgid "Freelook Slow Modifier" msgstr "Modificador de Velocidad de Vista Libre" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Cambiar Tamaño de Cámara" +msgstr "Alternar Vista Previa de la Cámara" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8115,9 +8083,8 @@ msgstr "" "No se puede utilizar como un indicador fiable del rendimiento en el juego." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Convertir a %s" +msgstr "Convertir Rooms" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8139,7 +8106,6 @@ msgstr "" "opacas (\"x-ray\")." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Ajustar Nodos al Suelo" @@ -8157,7 +8123,7 @@ msgstr "Usar Snap" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Convertir rooms para eliminar portales." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8253,9 +8219,8 @@ msgid "View Grid" msgstr "Ver CuadrÃcula" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Configuración de ventanilla" +msgstr "Ver Eliminación de Portales" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8576,221 +8541,196 @@ msgid "TextureRegion" msgstr "Región de Textura" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Color" +msgstr "Colores" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "TipografÃa" +msgstr "Fonts" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Icono" +msgstr "Icons" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Caja de estilos" +msgstr "Styleboxes" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} color(es)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "No se encontró ningún sub-recurso." +msgstr "No se encontraron colores." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "Constantes" +msgstr "{num} constant(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Constante de color." +msgstr "No se encontraron constants." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} font(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "¡No se ha encontrado!" +msgstr "No se han encontrado fonts." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} icon(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "¡No se ha encontrado!" +msgstr "No se han encontrado icons." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} stylebox(es)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "No se encontró ningún sub-recurso." +msgstr "No se encontraron styleboxes." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} seleccionado actualmente" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "No se ha seleccionado nada para la importación." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Importar Tema" +msgstr "Importar Elementos del Theme" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Importación de elementos {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "¿Salir del editor?" +msgstr "Actualización del editor" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Analizando" +msgstr "Finalizando" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Filtro: " +msgstr "Filtro:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "Con Datos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Selecciona un Nodo" +msgstr "Seleccionar por tipo de datos:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Selecciona una división para borrarla." +msgstr "Seleccionar todos los elementos color visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos color visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "Deseleccionar todos los elementos color visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos elementos constant visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos constant visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "Deseleccionar todos los elementos constant visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos los elementos font visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos font visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "Deseleccionar todos los elementos font visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos los elementos icon visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos los elementos icon visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Deseleccionar todos los elementos icon visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Seleccionar todos los elementos stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos stylebox visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "Deseleccionar todos los elementos stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Precaución: Añadir datos de iconos puede aumentar considerablemente el " +"tamaño de su recurso Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Colapsar Todo" +msgstr "Tipos de colapso." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Expandir Todo" +msgstr "Expandir tipos." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Selecciona un Archivo de Plantilla" +msgstr "Seleccionar todos los elementos del Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Seleccionar Puntos" +msgstr "Seleccionar Con Datos" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Seleccionar todos los elementos del Theme con los datos del elemento." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Seleccionar Todo" +msgstr "Deseleccionar Todo" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "Deseleccionar todos los elementos del Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Importar Escena" +msgstr "Importar Seleccionado" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8798,271 +8738,248 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"En la pestaña de Elementos de Importación se han seleccionado algunos " +"elementos. La selección se perderá al cerrar esta ventana.\n" +"¿Cerrar de todos modos?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos de Color" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Eliminar Ãtem" +msgstr "Cambiar Nombre de Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos Constant" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos Font" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos de Iconos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos de StyleBox" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Añadir Clases de Ãtems" +msgstr "Añadir Elemento Color" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Añadir Clases de Ãtems" +msgstr "Añadir Elemento Constant" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento Font" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento Icono" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Añadir Todos los Ãtems" +msgstr "Añadir Elemento Stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Eliminar Clases de Ãtems" +msgstr "Cambiar Nombre del Elemento Color" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Eliminar Clases de Ãtems" +msgstr "Cambiar Nombre del Elemento Constant" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Renombrar Nodo" +msgstr "Renombrar Elemento Font" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Renombrar Nodo" +msgstr "Renombrar Elemento Icon" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Eliminar Ãtem Seleccionado" +msgstr "Renombrar Elemento Stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "Archivo inválido. No es un layout de bus de audio." +msgstr "Archivo inválido, no es un recurso del Theme." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "Archivo inválido, igual que el recurso del Theme editado." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Administrar Plantillas" +msgstr "Administrar Elementos del Theme" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Ãtem Editable" +msgstr "Editar Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Tipo:" +msgstr "Tipos:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Tipo:" +msgstr "Añadir Tipo:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Añadir Todos los Ãtems" +msgstr "Añadir Elemento StyleBox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Eliminar Ãtem" +msgstr "Eliminar Elemento:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "Eliminar Clases de Ãtems" +msgstr "Eliminar Clases de Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Eliminar Clases de Ãtems" +msgstr "Eliminar Elementos Personalizados" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Elementos del tema de interfaz" +msgstr "Añadir Elemento del Theme" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Nombre del Nodo:" +msgstr "Nombre Antiguo:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Importar Tema" +msgstr "Importar Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Predeterminado" +msgstr "Theme Predeterminado" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Editar Tema" +msgstr "Editor de Themes" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Eliminar Recurso" +msgstr "Seleccionar Otro Recurso del Theme:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Importar Tema" +msgstr "Otro Theme" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Renombrar pista de animación" +msgstr "Confirmar Cambio de Nombre del Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Renombrar por lote" +msgstr "Cancelar Cambio de Nombre del Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Anulaciones" +msgstr "Elemento de Anulación" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Quitar este StyleBox como estilo principal." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Establecer este StyleBox como un estilo principal. La edición de sus " +"propiedades actualizará las mismas propiedades en todos los demás StyleBoxes " +"de este tipo." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Tipo" +msgstr "Añadir Tipo" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Añadir Ãtem" +msgstr "Añadir Tipo de Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Tipo de nodo" +msgstr "Tipo de nodo:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Cargar Valores por Defecto" +msgstr "Mostrar Por Defecto" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Mostrar los elementos de tipo por defecto junto a los elementos que han sido " +"anulados." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Anulaciones" +msgstr "Anular Todo" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Anular todos los elementos de tipo por defecto." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Tema" +msgstr "Theme:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Administrar Plantillas de Exportación..." +msgstr "Administrar Elementos..." #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Añadir, eliminar, organizar e importar elementos del Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Vista Previa" +msgstr "Añadir Vista Previa" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Actualizar Vista Previa" +msgstr "Vista Previa Por Defecto" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Selecciona una Malla de Origen:" +msgstr "Seleccionar Escena UI:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Activar el selector de controles, lo que permite seleccionar visualmente los " +"tipos de control para su edición." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9074,32 +8991,31 @@ msgstr "Botón Desactivado" #: editor/plugins/theme_editor_preview.cpp msgid "Item" -msgstr "Ãtem" +msgstr "Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Disabled Item" -msgstr "Desactivar Ãtem" +msgstr "Desactivar Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Check Item" -msgstr "Activar Ãtem" +msgstr "Activar Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Checked Item" -msgstr "Ãtem Activado" +msgstr "Elemento Activado" #: editor/plugins/theme_editor_preview.cpp msgid "Radio Item" -msgstr "Radio Ãtem" +msgstr "Radio Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Checked Radio Item" -msgstr "Radio Ãtem Activo" +msgstr "Radio Elemento Activo" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Separador con nombre." +msgstr "Separador con nombre" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9107,11 +9023,11 @@ msgstr "Submenú" #: editor/plugins/theme_editor_preview.cpp msgid "Subitem 1" -msgstr "SubÃtem 1" +msgstr "Subelemento 1" #: editor/plugins/theme_editor_preview.cpp msgid "Subitem 2" -msgstr "SubÃtem 2" +msgstr "Subelemento 2" #: editor/plugins/theme_editor_preview.cpp msgid "Has" @@ -9139,7 +9055,7 @@ msgstr "Tab 3" #: editor/plugins/theme_editor_preview.cpp msgid "Editable Item" -msgstr "Ãtem Editable" +msgstr "Elemento Editable" #: editor/plugins/theme_editor_preview.cpp msgid "Subtree" @@ -9152,19 +9068,19 @@ msgstr "Tienes, muchas, opciones" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Ruta inválida, el recurso PackedScene probablemente fue movido o eliminado." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." -msgstr "" +msgstr "Recurso PackedScene inválido, debe tener un nodo Control en raÃz." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "Archivo inválido. No es un layout de bus de audio." +msgstr "Archivo inválido, no es un recurso PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Recargar la escena para reflejar su estado actual." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10569,9 +10485,8 @@ msgid "VisualShader" msgstr "VisualShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Editar Propiedad Visual" +msgstr "Editar Propiedad Visual:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10698,9 +10613,8 @@ msgid "Script" msgstr "Script" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Modo de exportación de scripts:" +msgstr "Modo de Exportación GDScript:" #: editor/project_export.cpp msgid "Text" @@ -10708,21 +10622,20 @@ msgstr "Texto" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Bytecode Compilado (Carga Más Rápida)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Encriptado (Proveer la Clave Debajo)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Llave de Encriptación Inválida (debe tener 64 caracteres de largo)" +msgstr "" +"Llave de Encriptación Inválida (debe tener 64 caracteres hexadecimales)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Clave de cifrado de scripts (256-bits en hexadecimal):" +msgstr "Llave de Encriptación GDScript (256-bits en hexadecimal):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10796,7 +10709,6 @@ msgid "Imported Project" msgstr "Proyecto Importado" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Nombre de Proyecto Inválido." @@ -11015,7 +10927,7 @@ msgid "" "Can't run project: Assets need to be imported.\n" "Please edit the project to trigger the initial import." msgstr "" -"No se puede ejecutar el proyecto: los assets necesitan ser importados.\n" +"No se puede ejecutar el proyecto: Los assets necesitan ser importados.\n" "Por favor, edita el proyecto para activar el importado inicial." #: editor/project_manager.cpp @@ -11023,14 +10935,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "¿Estás seguro de que vas a ejecutar %d proyectos a la vez?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Seleccionar dispositivo de la lista" +msgstr "¿Quitar %d proyectos de la lista?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Seleccionar dispositivo de la lista" +msgstr "¿Quitar este proyecto de la lista?" #: editor/project_manager.cpp msgid "" @@ -11064,9 +10974,8 @@ msgid "Project Manager" msgstr "Administrador de Proyectos" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Proyectos" +msgstr "Proyectos Locales" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11077,23 +10986,20 @@ msgid "Last Modified" msgstr "Ultima Modificación" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "Exportar Proyecto" +msgstr "Editar Proyecto" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Renombrar Proyecto" +msgstr "Reproducir Proyecto" #: editor/project_manager.cpp msgid "Scan" msgstr "Escanear" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Proyectos" +msgstr "Escanear Proyectos" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11104,14 +11010,12 @@ msgid "New Project" msgstr "Nuevo Proyecto" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Proyecto Importado" +msgstr "Importar Proyecto" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Renombrar Proyecto" +msgstr "Eliminar Proyecto" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11122,9 +11026,8 @@ msgid "About" msgstr "Acerca de" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Biblioteca de Assets" +msgstr "Proyectos de la LibrerÃa de Assets" #: editor/project_manager.cpp msgid "Restart Now" @@ -11136,7 +11039,7 @@ msgstr "Eliminar Todos" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "También eliminar el contenido del proyecto (¡no se puede deshacer!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11148,24 +11051,22 @@ msgid "" "Would you like to explore official example projects in the Asset Library?" msgstr "" "Actualmente no tienes ningún proyecto.\n" -"¿Quieres explorar proyectos de ejemplo oficiales en la Biblioteca de Assets?" +"¿Quieres explorar proyectos de ejemplo oficiales en la LibrerÃa de Assets?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Filtrar propiedades" +msgstr "Filtrar proyectos" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"La casilla de búsqueda filtra los proyectos por nombre y el último " -"componente de la ruta.\n" -"Para filtrar los proyectos por nombre y ruta completa, la consulta debe " -"contener al menos un carácter `/`." +"Este campo filtra los proyectos por nombre y por el último componente de la " +"ruta.\n" +"Para filtrar proyectos por nombre y ruta completa, la consulta debe contener " +"al menos un carácter `/`." #: editor/project_settings_editor.cpp msgid "Key " @@ -11173,7 +11074,7 @@ msgstr "Tecla " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Tecla FÃsica" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11221,7 +11122,7 @@ msgstr "Dispositivo" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (FÃsica)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11321,7 +11222,7 @@ msgstr "Añadir Propiedad Global" #: editor/project_settings_editor.cpp msgid "Select a setting item first!" -msgstr "¡Selecciona un Ãtem primero!" +msgstr "¡Selecciona un elemento primero!" #: editor/project_settings_editor.cpp msgid "No property '%s' exists." @@ -11333,7 +11234,7 @@ msgstr "El ajuste '%s' es interno y no puede ser eliminado." #: editor/project_settings_editor.cpp msgid "Delete Item" -msgstr "Eliminar Ãtem" +msgstr "Eliminar Elemento" #: editor/project_settings_editor.cpp msgid "" @@ -11364,23 +11265,20 @@ msgid "Override for Feature" msgstr "Anulación de la CaracterÃstica" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Añadir Traducción" +msgstr "Añadir %d Traducciones" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Eliminar Traducción" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Añadir Remapeo de Recursos" +msgstr "Remapeo de Recursos de Traducción: Añadir %d Ruta(s)" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Añadir Remapeo de Recursos" +msgstr "Remapeo de Recursos de Traducción: Añadir %d Remapeo(s)" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11825,12 +11723,16 @@ msgstr "¿Eliminar nodo \"%s\"?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Guardar la rama como una escena requiere tener una escena abierta en el " +"editor." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Guardar la rama como una escena requiere seleccionar sólo un nodo, pero has " +"seleccionado %d nodos." #: editor/scene_tree_dock.cpp msgid "" @@ -11839,6 +11741,11 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"No se puede guardar la rama del nodo raÃz como una escena instanciada.\n" +"Para crear una copia editable de la escena actual, duplÃcala usando el menú " +"contextual del dock Sistema de Archivos\n" +"o crea una escena heredada usando Escena > Nueva Escena Heredada... en su " +"lugar." #: editor/scene_tree_dock.cpp msgid "" @@ -11846,6 +11753,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"No se puede guardar la rama de una escena ya instanciada.\n" +"Para crear una variación de una escena, puedes hacer una escena heredada " +"basada en la escena instanciada usando Escena > Nueva Escena Heredada... en " +"su lugar." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12254,6 +12165,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Advertencia: No es recomendable que el nombre del script sea el mismo que el " +"de un tipo integrado." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12325,7 +12238,7 @@ msgstr "Copiar Error" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Código Abierto C++ en GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12369,7 +12282,7 @@ msgstr "Monitores" #: editor/script_editor_debugger.cpp msgid "Pick one or more items from the list to display the graph." -msgstr "Elige uno o más Ãtems de la lista para mostrar el gráfico." +msgstr "Elige uno o más elementos de la lista para mostrar el gráfico." #: editor/script_editor_debugger.cpp msgid "List of Video Memory Usage by Resource:" @@ -12503,6 +12416,16 @@ msgstr "Cambiar Altura de la Forma del Cilindro" msgid "Change Ray Shape Length" msgstr "Cambiar Longitud de la Forma del Rayo" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Establecer Posición de Punto de Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Establecer Posición de Punto de Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Cambiar Radio de Cylinder" @@ -12616,14 +12539,12 @@ msgid "Object can't provide a length." msgstr "El objeto no puede proporcionar una longitud." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "Exportar LibrerÃa de Mallas" +msgstr "Exportar Malla GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "Exportar…" +msgstr "Exportar GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12666,9 +12587,8 @@ msgid "GridMap Paint" msgstr "Pintar GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Rellenar Selección en GridMap" +msgstr "Seleccionar GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12922,14 +12842,12 @@ msgid "Add Output Port" msgstr "Añadir Puerto de Salida" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Cambiar Tipo" +msgstr "Cambiar Tipo de Puerto" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Cambiar nombre del puerto de entrada" +msgstr "Cambiar Nombre de Puerto" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13044,9 +12962,8 @@ msgid "Add Preload Node" msgstr "Añadir Nodo Preload" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Añadir Nodo" +msgstr "Añadir Nodo(s)" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13313,37 +13230,31 @@ msgstr "Seleccionar dispositivo de la lista" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "Ejecutar en %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "Exportar Todo" +msgstr "Exportar APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Desinstalar" +msgstr "Desinstalando..." #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Cargando, espera por favor..." +msgstr "Instalando en el dispositivo, espera por favor..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "¡No se pudo instanciar la escena!" +msgstr "No se pudo instalar en el dispositivo: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "Ejecutando Script Personalizado..." +msgstr "Ejecutando en el dispositivo..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "No se pudo crear la carpeta." +msgstr "No se ha podido ejecutar en el dispositivo." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13473,45 +13384,43 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"No se ha encontrado 'apksigner'.\n" +"Por favor, compruebe que el comando está disponible en el directorio Android " +"SDK build-tools.\n" +"El resultado %s es sin firma." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "Firma de depuración %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Escaneando archivos,\n" -"Por favor, espere..." +msgstr "Firmando liberación %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "No se pudo abrir la plantilla para exportar:" +msgstr "No se pudo encontrar la keystore, no se puedo exportar." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "'apksigner' ha retornado con error #%d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "Añadiendo %s..." +msgstr "Verificando %s..." #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "La verificación de 'apksigner' de %s ha fallado." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "Exportar Todo" +msgstr "Exportando para Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." msgstr "" -"¡Nombre de archivo inválido! Android App Bundle requiere la extensión *.aab." +"¡Nombre del archivo inválido! Android App Bundle requiere la extensión *.aab." #: platform/android/export/export.cpp msgid "APK Expansion not compatible with Android App Bundle." @@ -13523,7 +13432,7 @@ msgstr "¡Nombre de archivo inválido! Android APK requiere la extensión *.apk. #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "¡Formato de exportación no compatible!\n" #: platform/android/export/export.cpp msgid "" @@ -13551,16 +13460,16 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"No se puede sobrescribir los archivos res://android/build/res/*.xml con el " +"nombre del proyecto" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "No se encontró project.godot en la ruta del proyecto." +msgstr "No se pueden exportar los archivos del proyecto a un proyecto gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "No se puede escribir en el archivo:" +msgstr "¡No se pudo escribir el archivo del paquete de expansión!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13588,21 +13497,20 @@ msgstr "" "directorio del proyecto de gradle para ver los resultados." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "No se encontró la animación: '%s'" +msgstr "Paquete no encontrado:% s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Creando contornos..." +msgstr "Creando APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "No se pudo abrir la plantilla para exportar:" +msgstr "" +"No se pudo encontrar la plantilla APK para exportar:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13611,16 +13519,18 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Faltan bibliotecas en la plantilla de exportación para las arquitecturas " +"seleccionadas: %s.\n" +"Por favor, construya una plantilla con todas las bibliotecas necesarias, o " +"desmarque las arquitecturas que faltan en el preajuste de exportación." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "Añadiendo %s..." +msgstr "Añadiendo archivos ..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "No se puede escribir en el archivo:" +msgstr "No se pudieron exportar los archivos del proyecto" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13628,15 +13538,15 @@ msgstr "Alineando APK..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "No se pudo descomprimir el APK no alineado temporal." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." -msgstr "Identificador no encontrado." +msgstr "Falta el identificador." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "The character '%s' is not allowed in Identifier." -msgstr "El carácter '%s' no esta permitido como identificador." +msgstr "El carácter '% s' no está permitido en el Identificador." #: platform/iphone/export/export.cpp msgid "App Store Team ID not specified - cannot configure the project." @@ -13676,45 +13586,40 @@ msgid "Could not write file:" msgstr "No se puede escribir en el archivo:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "No se puede escribir en el archivo:" +msgstr "No se pudo leer el archivo:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "No se puede leer shell HTML personalizada:" +msgstr "No se pudo leer el shell HTML:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "No se pudo crear la carpeta." +msgstr "No se pudo crear el directorio del servidor HTTP:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Error al guardar escena." +msgstr "Error al iniciar el servidor HTTP:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Identificador inválido:" +msgstr "Identificador de paquete no válido:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "Notarización: se requiere firma de código." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "Notarización: se requiere tiempo de ejecución reforzado." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "Notarización: nombre de ID de Apple no especificado." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "Notarización: contraseña de ID de Apple no especificada." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14162,6 +14067,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"La propiedad Compress de GIProbe ha quedado obsoleta debido a errores " +"conocidos y ya no tiene ningún efecto.\n" +"Para eliminar esta advertencia, desactiva la propiedad Compress de GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14253,15 +14161,15 @@ msgstr "El nodo A y el nodo B deben ser diferentes PhysicsBody" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "El RoomManager no debe ser hijo o nieto de un Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Una Room no debe ser hijo o nieto de un Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Un RoomGroup no debe ser hijo o nieto de un Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14273,42 +14181,83 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." -msgstr "" +msgstr "Una Room no puede tener otra Room como hija o nieta." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "El RoomManager no debe ubicarse dentro de una Room." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "Un RoomGroup no debe colocarse dentro de una Room." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"El cuerpo convexo de la room contiene un gran número de planos.\n" +"Considera la posibilidad de simplificar los lÃmites de la room para aumentar " +"el rendimiento." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "El RoomManager no debe colocarse dentro de un RoomGroup." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "La RoomList no ha sido asignada." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "El nodo RoomList debe ser un Spatial (o derivado de Spatial)." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"El LÃmite de Profundidad del Portal está ajustado a cero.\n" +"Sólo se renderizará la room en la que se encuentra la cámara." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "Sólo debe haber un RoomManager en el SceneTree." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14375,7 +14324,7 @@ msgstr "No se encontró la animación: '%s'" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "Aplicar Reset de la Animación" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14553,25 +14502,30 @@ msgid "Invalid comparison function for that type." msgstr "Función de comparación inválida para este tipo." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Solo se pueden asignar variaciones en funciones de vértice." +msgstr "No se puede asignar la variable en la función '%s'." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Las variaciones asignadas en función 'vértice' no pueden reasignarse en " +"'fragmento' o 'luz'." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Varyings Cuál asignó en 'fragmento' la función no puede ser reasignada en " +"'vértice' o 'ligero'." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" msgstr "" +"¡No se pudo acceder a la variación de la etapa de fragmento en la función " +"personalizada!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15771,9 +15725,6 @@ msgstr "Las constantes no pueden modificarse." #~ msgid "I see..." #~ msgstr "Ya veo..." -#~ msgid "Can't open '%s'." -#~ msgstr "No se puede abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Vaya" diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po index 53041c74fd..d5c955a347 100644 --- a/editor/translations/es_AR.po +++ b/editor/translations/es_AR.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-06 06:47+0000\n" "Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n" "Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/" "godot-engine/godot/es_AR/>\n" @@ -30,7 +30,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -353,6 +353,7 @@ msgstr "Cambiar Modo de Bucle de Animación" msgid "Remove Anim Track" msgstr "Quitar pista de animación" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Crear NUEVO track para %s e insertar clave?" @@ -377,10 +378,28 @@ msgstr "Crear" msgid "Anim Insert" msgstr "Insertar Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "No se puede abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animación" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Un AnimationPlayer no puede animarse a sà mismo, solo a otros players." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "No existe la propiedad '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crear e Insertar Animación" @@ -591,9 +610,8 @@ msgid "Go to Previous Step" msgstr "Ir a Paso Previo" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Resetear" +msgstr "Aplicar Reset" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -612,9 +630,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Pegar Pistas" +msgstr "Crear RESET Track(s)" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -940,9 +957,8 @@ msgid "Edit..." msgstr "Editar..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" -msgstr "Ir Al Método" +msgstr "Ir al Método" #: editor/create_dialog.cpp msgid "Change %s Type" @@ -960,9 +976,9 @@ msgstr "Crear Nuevo %s" msgid "No results for \"%s\"." msgstr "No hay resultados para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "No hay descripción disponible para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1260,9 +1276,8 @@ msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "Error al abrir el archivo de paquete (no esta en formato ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Ya existe)" +msgstr "%s (ya existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" @@ -1277,19 +1292,16 @@ msgid "Uncompressing Assets" msgstr "Descomprimiendo Assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Los siguientes archivos no se pudieron extraer del paquete:" +msgstr "Falló la extracción de los siguientes archivos del asset \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Y %d archivos más." +msgstr "(y %s archivos más)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "El Paquete se instaló exitosamente!" +msgstr "El asset \"%s\" se instaló exitosamente!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1301,9 +1313,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de Paquetes" +msgstr "Instalador de Assets" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1366,7 +1377,6 @@ msgid "Bypass" msgstr "Bypass" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Opciones de Bus" @@ -1491,7 +1501,7 @@ msgstr "No debe coincidir con el nombre de una clase ya existente del motor." #: editor/editor_autoload_settings.cpp msgid "Must not collide with an existing built-in type name." -msgstr "No debe coincidir con el nombre de un tipo built-in ya existente." +msgstr "No debe coincidir con el nombre de un tipo integrado ya existente." #: editor/editor_autoload_settings.cpp msgid "Must not collide with an existing global constant name." @@ -1534,13 +1544,12 @@ msgid "Can't add autoload:" msgstr "No se puede agregar autoload:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "El archivo existe." +msgstr "%s es una ruta inválida. El archivo no existe." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s es una ruta inválida. No esta en la ruta de recursos (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1564,9 +1573,8 @@ msgid "Name" msgstr "Nombre" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Variable" +msgstr "Variable Global" #: editor/editor_data.cpp msgid "Paste Params" @@ -1742,29 +1750,32 @@ msgstr "Dock de Importación" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite ver y editar escenas 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Permite editar scripts usando el editor de scripts integrado." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Provee acceso integrado a la Biblioteca de Assets." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permite editar la jerarquÃa de nodos en el panel de Escena." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Permite trabajar con señales y grupos del nodo seleccionado en el panel de " +"Escena." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permite explorar el sistema de archivos local a través de un panel dedicado." #: editor/editor_feature_profile.cpp msgid "" @@ -1773,13 +1784,12 @@ msgid "" msgstr "" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Actual)" +msgstr "(actual)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(ninguno)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." @@ -1815,19 +1825,16 @@ msgid "Enable Contextual Editor" msgstr "Activar el Editor Contextual" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Propiedades:" +msgstr "Propiedades de Clase:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "CaracterÃsticas" +msgstr "CaracterÃsticas Principales:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Clases Activadas:" +msgstr "Nodos y Clases:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1847,23 +1854,20 @@ msgid "Error saving profile to path: '%s'." msgstr "Error al guardar el perfil en la ruta: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Restablecer Valores Por Defecto" +msgstr "Restablecer a Valores Por Defecto" #: editor/editor_feature_profile.cpp msgid "Current Profile:" msgstr "Perfil Actual:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Borrar Perfil" +msgstr "Crear Perfil" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Remover Tile" +msgstr "Remover Perfil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1883,14 +1887,12 @@ msgid "Export" msgstr "Exportar" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Perfil Actual:" +msgstr "Configurar Perfil Seleccionado:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Opciones de Textura" +msgstr "Opciones Extra:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." @@ -1921,9 +1923,8 @@ msgid "Select Current Folder" msgstr "Seleccionar Carpeta Actual" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "El Archivo Existe, Sobreescribir?" +msgstr "El archivo existe, sobrescribir?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2316,6 +2317,17 @@ msgid "New Window" msgstr "Nueva Ventana" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira cuando la ventana del editor se redibuja." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Los recursos importados no se pueden guardar." @@ -2944,9 +2956,8 @@ msgid "Orphan Resource Explorer..." msgstr "Explorador de Recursos Huérfanos..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Renombrar Proyecto" +msgstr "Volver a Cargar el Proyecto Actual" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3106,22 +3117,20 @@ msgid "Help" msgstr "Ayuda" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Abrir Documentación" +msgstr "Documentación En LÃnea" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Preguntas y Respuestas" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Reportar un Bug" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Setear un Valor" +msgstr "Sugerir una Feature" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3132,9 +3141,8 @@ msgid "Community" msgstr "Comunidad" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Acerca de" +msgstr "Acerca de Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3186,10 +3194,6 @@ msgid "Save & Restart" msgstr "Guardar y Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira cuando la ventana del editor se redibuja." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizar Continuamente" @@ -3232,9 +3236,8 @@ msgid "Manage Templates" msgstr "Administrar Plantillas" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde archivo" #: editor/editor_node.cpp #, fuzzy @@ -3323,9 +3326,8 @@ msgid "Select" msgstr "Seleccionar" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Seleccionar Carpeta Actual" +msgstr "Seleccionar Actual" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3389,14 +3391,12 @@ msgid "Update" msgstr "Actualizar" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Version:" +msgstr "Version" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Autores" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3414,7 +3414,6 @@ msgid "Frame Time (ms)" msgstr "Duración de Frame (seg)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" msgstr "Tiempo Promedio (seg)" @@ -3564,9 +3563,8 @@ msgid "Paste" msgstr "Pegar" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" -msgstr "Convertir A %s" +msgstr "Convertir a %s" #: editor/editor_resource_picker.cpp editor/property_editor.cpp msgid "New %s" @@ -3640,49 +3638,43 @@ msgstr "Importar Desde Nodo:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Abrir la carpeta que contiene estas plantillas." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Desinstalar estas plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "No hay ningún archivo '%s'." +msgstr "No hay mirrors disponibles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Recuperando mirrors, esperá, por favor..." +msgstr "Obteniendo la lista de mirrors..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Iniciando la descarga..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Error al solicitar la URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Conectando al Mirror..." +msgstr "Conectando al mirror..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "No se ha podido resolver el nombre del host:" +msgstr "No se pudo resolver la dirección indicada." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "No se puede conectar al host:" +msgstr "No se puede conectar al mirror." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "No hay respuesta desde el host:" +msgstr "No hubo respuesta desde el mirror." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3690,18 +3682,16 @@ msgid "Request failed." msgstr "Solicitud fallida." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Solicitud fallida, demasiadas redireccinoes" +msgstr "La solicitud termino en un bucle de redirecciones." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Solicitud fallida." +msgstr "Solicitud fallida:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Descarga completada; extrayendo plantillas..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3728,7 +3718,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Mejor mirror disponible" #: editor/export_template_manager.cpp msgid "" @@ -3828,15 +3818,17 @@ msgstr "Version Actual:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Faltan las plantillas de exportación. Descargalas o instalalas desde un " +"archivo." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." msgstr "" +"Las plantillas de exportación están instaladas y listas para ser usadas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Abrir Archivo" +msgstr "Abrir Carpeta" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." @@ -3852,13 +3844,22 @@ msgid "Uninstall templates for the current version." msgstr "Valor inicial para el contador" #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Error de Descarga" +msgstr "Descargar desde:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ejecutar en el Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Error" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Descargar e Instalar" #: editor/export_template_manager.cpp msgid "" @@ -3873,14 +3874,12 @@ msgstr "" "versiones de desarrollo." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde Archivo" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Importar Plantillas Desde Archivo ZIP" +msgstr "Instalar plantillas desde un archivo local." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3888,19 +3887,16 @@ msgid "Cancel" msgstr "Cancelar" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "No se puede abir el zip de plantillas de exportación." +msgstr "Cancelar la descarga de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Versiones Instaladas:" +msgstr "Otras Versiones Instaladas:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Desinstalar" +msgstr "Desinstalar Plantilla" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -4062,35 +4058,32 @@ msgid "Collapse All" msgstr "Colapsar Todos" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Buscar archivos" +msgstr "Ordenar archivos" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Ordenar por Nombre (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Ordenar por Nombre (Descendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Ordenar por Tipo (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Ordenar por Tipo (Descendente)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Ultima Modificación" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Primera Modificación" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4102,7 +4095,7 @@ msgstr "Renombrar..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Ubicar foco en la caja de búsqueda" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4412,14 +4405,12 @@ msgid "Failed to load resource." msgstr "Fallo al cargar recurso." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Propiedades" +msgstr "Copiar Propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Propiedades" +msgstr "Pegar Propiedades" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4444,23 +4435,20 @@ msgid "Save As..." msgstr "Guardar Como..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "No está en la ruta de recursos." +msgstr "Opciones de recursos extra." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Editar Portapapeles de Recursos" +msgstr "Editar Recurso desde el Portapapeles" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Copiar Recurso" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Crear Built-In" +msgstr "Convertir en Recurso Integrado" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4475,9 +4463,8 @@ msgid "History of recently edited objects." msgstr "Historial de objetos recientemente editados." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Abrir Documentación" +msgstr "Abrir la documentación para este objeto." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4488,9 +4475,8 @@ msgid "Filter properties" msgstr "Filtrar propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Propiedades del objeto." +msgstr "Administrar propiedades del objeto." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4735,9 +4721,8 @@ msgid "Blend:" msgstr "Blend:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Parámetro Modificado" +msgstr "Parámetro Modificado:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5471,11 +5456,11 @@ msgstr "Todos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Buscar plantillas, proyectos y demos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Buscar assets (excluyendo plantillas, proyectos y demos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -6124,14 +6109,12 @@ msgid "Clear Pose" msgstr "Restablecer Pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Agregar Nodo" +msgstr "Agregar Nodo Acá" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instanciar Escena(s)" +msgstr "Instanciar Escena Acá" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6147,49 +6130,43 @@ msgstr "Panear Vista" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Zoom a 3.125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Zoom a 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Zoom a 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Alejar Zoom" +msgstr "Zoom a 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Alejar Zoom" +msgstr "Zoom a 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Alejar Zoom" +msgstr "Zoom a 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Alejar Zoom" +msgstr "Zoom a 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Alejar Zoom" +msgstr "Zoom a 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Alejar Zoom" +msgstr "Zoom a 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Zoom a 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -8734,6 +8711,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Quitar Todos los Ãtems" @@ -8764,6 +8747,12 @@ msgid "Remove All StyleBox Items" msgstr "Quitar Todos los Ãtems" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Agregar Items de Clases" @@ -12188,6 +12177,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Advertencia: Usualmente no se recomienda que un script tenga el mismo nombre " +"que un tipo integrado." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12437,6 +12428,16 @@ msgstr "Cambiar Altura de Shape Cilindro" msgid "Change Ray Shape Length" msgstr "Cambiar Largo de Shape Rayo" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Setear Posición de Punto de Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Setear Posición de Punto de Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Cambiar Radio de Cilindro" @@ -14233,6 +14234,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este cuerpo será ignorado hasta que se establezca un mesh." @@ -15498,9 +15535,6 @@ msgstr "Las constantes no pueden modificarse." #~ msgid "I see..." #~ msgstr "Ya Veo..." -#~ msgid "Can't open '%s'." -#~ msgstr "No se puede abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/et.po b/editor/translations/et.po index 6f8561f8ab..13019cd9e3 100644 --- a/editor/translations/et.po +++ b/editor/translations/et.po @@ -340,6 +340,7 @@ msgstr "Muuda animatsiooni silmuse režiimi" msgid "Remove Anim Track" msgstr "Eemalda animatsiooni rada" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Loo uus rada %s-le ja sisesta võti?" @@ -364,10 +365,27 @@ msgstr "Loo" msgid "Anim Insert" msgstr "Animatsiooni sisestus" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animatsioon" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ei saa ennast animeerida, ainult teise mänijaid." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Atribuut" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Loo ja sisesta animatsioon" @@ -930,7 +948,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2261,6 +2279,17 @@ msgid "New Window" msgstr "Uus aken" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Imporditud ressursse ei saa salvestada." @@ -3057,10 +3086,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3673,6 +3698,16 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ava failihalduris" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopeeri viga" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8388,6 +8423,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Eemalda kõik katkepunktid" @@ -8418,6 +8459,12 @@ msgid "Remove All StyleBox Items" msgstr "Eemalda kõik katkepunktid" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Lisa lemmikutesse" @@ -11868,6 +11915,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13479,6 +13534,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/eu.po b/editor/translations/eu.po index 018f65b79e..7b6934ff33 100644 --- a/editor/translations/eu.po +++ b/editor/translations/eu.po @@ -340,6 +340,7 @@ msgstr "Animazioaren Loop Modua Aldatu" msgid "Remove Anim Track" msgstr "Ezabatu Animazio Pista" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s-rentzat pista berria sortu eta giltza sartu?" @@ -364,10 +365,27 @@ msgstr "Sortu" msgid "Anim Insert" msgstr "Animazioa Sartu" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Kargatu animazioa" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer bat ezin da norbera animatu, soilik beste playerrak." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Propietateak" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animazioa Sortu eta Txertatu" @@ -929,7 +947,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2242,6 +2260,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3025,10 +3054,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3648,6 +3673,15 @@ msgid "Download from:" msgstr "Jaitsiera errorea" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ireki fitxategi-kudeatzailean" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8355,6 +8389,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Kendu elementu guztiak" @@ -8385,6 +8425,12 @@ msgid "Remove All StyleBox Items" msgstr "Kendu elementu guztiak" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11835,6 +11881,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13441,6 +13495,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/fa.po b/editor/translations/fa.po index 09cc83a73b..bb761cf137 100644 --- a/editor/translations/fa.po +++ b/editor/translations/fa.po @@ -357,6 +357,7 @@ msgstr "تغییر ØØ§Ù„ت تکررار (Loop) انیمیشن" msgid "Remove Anim Track" msgstr "ØØ°Ù ترک انیمشین" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "یک ترک جدید برای s% بساز Ùˆ کلید را درج کن؟" @@ -381,10 +382,28 @@ msgstr "تولید" msgid "Anim Insert" msgstr "در انیمیشن درج Ú©Ù†" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "در ØØ§Ù„ اتصال..." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "انیمیشن" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "انیمیشن پلیر نمی تواند خود را انیمیت کند. Ùقط پلیر دیگر." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ویژگی '%s' موجود نیست." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "ساختن Ùˆ درج انیمیشن" @@ -959,7 +978,7 @@ msgstr "ساختن %s جدید" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2282,6 +2301,17 @@ msgid "New Window" msgstr "چارچوب جدید" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3075,10 +3105,6 @@ msgid "Save & Restart" msgstr "ذخیره Ùˆ خروج" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "مستمر" @@ -3722,6 +3748,16 @@ msgid "Download from:" msgstr "خطاهای بارگیری" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "گشودن در مدیر پرونده" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "خطاهای بارگذاری" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8726,6 +8762,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "برداشتن انتخاب شده" @@ -8756,6 +8798,12 @@ msgid "Remove All StyleBox Items" msgstr "برداشتن انتخاب شده" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Ø§ÙØ²ÙˆØ¯Ù† مورد" @@ -12427,6 +12475,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "برداشتن موج" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "برداشتن موج" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14176,6 +14234,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14873,10 +14967,6 @@ msgstr "ثوابت قابل تغییر نیستند." #~ msgid "Thanks!" #~ msgstr "با تشکر !" -#, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "در ØØ§Ù„ اتصال..." - #~ msgid "Run Script" #~ msgstr "اجرای اسکریپت" diff --git a/editor/translations/fi.po b/editor/translations/fi.po index f0b5ff0457..ffedccec28 100644 --- a/editor/translations/fi.po +++ b/editor/translations/fi.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 21:48+0000\n" +"PO-Revision-Date: 2021-08-10 21:40+0000\n" "Last-Translator: Tapani Niemi <tapani.niemi@kapsi.fi>\n" "Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/" "godot/fi/>\n" @@ -25,7 +25,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -347,6 +347,7 @@ msgstr "Vaihda animaation toistotilaa" msgid "Remove Anim Track" msgstr "Poista animaatioraita" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Luo kohteelle %s UUSI raita ja lisää avain?" @@ -371,10 +372,28 @@ msgstr "Luo" msgid "Anim Insert" msgstr "Animaatio: lisää" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Ei voida avata tiedostoa '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animaatio" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ei voi animoida itseään, vain muita toistimia." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Ominaisuutta '%s' ei löytynyt." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animaatio: luo ja lisää" @@ -580,9 +599,8 @@ msgid "Go to Previous Step" msgstr "Mene edelliseen askeleeseen" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Palauta" +msgstr "Tee palautus" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -601,9 +619,8 @@ msgid "Use Bezier Curves" msgstr "Käytä Bezier-käyriä" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Liitä raidat" +msgstr "Luo palautusraidat" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -927,7 +944,6 @@ msgid "Edit..." msgstr "Muokkaa..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Mene metodiin" @@ -947,9 +963,9 @@ msgstr "Luo uusi %s" msgid "No results for \"%s\"." msgstr "Ei tuloksia haulle \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "%s kuvaus ei ole saatavilla." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1049,18 +1065,16 @@ msgid "Owners Of:" msgstr "Omistajat kohteelle:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Poista valitut tiedostot projektista? (ei voida kumota)\n" -"Löydät poistetut tiedostot järjestelmän roskakorista, mikäli haluat " -"palauttaa ne." +"Poista valitut tiedostot projektista? (Ei voida kumota.)\n" +"Riippuen tiedostojärjestelmäsi asetuksista, tiedostot siirretään joko " +"järjestelmän roskakoriin tai poistetaan pysyvästi." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1069,9 +1083,9 @@ msgid "" "to the system trash or deleted permanently." msgstr "" "Poistettavia tiedostoja tarvitaan muiden resurssien toimivuuteen.\n" -"Poistetaanko silti? (ei voida kumota)\n" -"Löydät poistetut tiedostot järjestelmän roskakorista, mikäli haluat " -"palauttaa ne." +"Poistetaanko ne silti? (Ei voida kumota.)\n" +"Riippuen tiedostojärjestelmäsi asetuksista, tiedostot siirretään joko " +"järjestelmän roskakoriin tai poistetaan pysyvästi." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1241,41 +1255,39 @@ msgid "Licenses" msgstr "Lisenssit" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Virhe avattaessa pakettitiedostoa (se ei ole ZIP-muodossa)." +msgstr "Virhe avattaessa \"%s\" asset-tiedostoa (se ei ole ZIP-muodossa)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (on jo olemassa)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Assetin \"%s\"sisältö - %d tiedostoa on ristiriidassa projektisi kanssa:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Assetin \"%s\" sisältö - Yksikään tiedosto ei ole ristiriidassa projektisi " +"kanssa:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Puretaan assetteja" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Seuraavien tiedostojen purku paketista epäonnistui:" +msgstr "Seuraavien tiedostojen purku assetista \"%s\" epäonnistui:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Ja vielä %s tiedostoa." +msgstr "(ja vielä %s tiedostoa)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Paketti asennettu onnistuneesti!" +msgstr "Asset \"%s\" asennettu onnistuneesti!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1287,9 +1299,8 @@ msgid "Install" msgstr "Asenna" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Pakettien asentaja" +msgstr "Assettien asentaja" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1352,7 +1363,6 @@ msgid "Bypass" msgstr "Ohita" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Väylän asetukset" @@ -1523,13 +1533,12 @@ msgid "Can't add autoload:" msgstr "Ei voida lisätä automaattisesti ladattavaa:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Tiedostoa ei ole olemassa." +msgstr "%s on virheellinen polku. Tiedostoa ei ole olemassa." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s on virheellinen polku. Ei löydy resurssipolusta (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1553,9 +1562,8 @@ msgid "Name" msgstr "Nimi" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Muuttuja" +msgstr "Globaali muuttuja" #: editor/editor_data.cpp msgid "Paste Params" @@ -1728,48 +1736,51 @@ msgstr "Tuontitelakka" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Antaa katsella ja muokata 3D-skenejä." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Antaa muokata skriptejä käyttäen integroitua skriptieditoria." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Tarjoaa valmiin pääsyn Asset-kirjastoon." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Antaa muokata solmuhierarkiaa Skene-telakassa." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Antaa työskennellä valitun solmun signaalien ja ryhmien kanssa Skene-" +"telakassa." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." -msgstr "" +msgstr "Antaa selata paikallista tiedostojärjestelmää erillisellä telakalla." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Antaa konfiguroida tuontiasetuksia yksittäiselle assetille. Edellyttää " +"Tiedostojärjestelmä-telakkaa toimiakseen." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Nykyinen)" +msgstr "(nykyinen)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(ei mikään)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "Poistetaanko nykyinen valittu profiili, '%s'? Ei voida kumota." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1802,19 +1813,16 @@ msgid "Enable Contextual Editor" msgstr "Ota asiayhteydellinen editori käyttöön" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Ominaisuudet:" +msgstr "Luokan ominaisuudet:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "Ominaisuudet" +msgstr "Pääominaisuudet:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Käytössä olevat luokat:" +msgstr "Solmut ja luokat:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1833,7 +1841,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Virhe tallennettaessa profiilia polkuun: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "Palauta oletusarvoihin" @@ -1842,14 +1849,12 @@ msgid "Current Profile:" msgstr "Nykyinen profiili:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Tyhjennä profiili" +msgstr "Luo profiili" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Poista laatta" +msgstr "Poista profiili" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1869,18 +1874,17 @@ msgid "Export" msgstr "Vie" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Nykyinen profiili:" +msgstr "Konfiguroi valittu profiili:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Tekstuurin asetukset" +msgstr "Ylimääräiset asetukset:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Luo tai tuo profiili muokataksesi saatavilla olevia luokkia ja ominaisuuksia." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1907,9 +1911,8 @@ msgid "Select Current Folder" msgstr "Valitse nykyinen kansio" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Tiedosto on jo olemassa, korvaa?" +msgstr "Tiedosto on jo olemassa, korvataanko?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2301,6 +2304,17 @@ msgid "New Window" msgstr "Uusi ikkuna" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Pyörii kun editorin ikkuna päivittyy." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Tuotuja resursseja ei voida tallentaa." @@ -2530,13 +2544,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"Nykyisellä skenellä ei ole juurisolmua, mutta %d muokattua ulkoista " +"resurssia tallennettiin silti." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Skenen tallentaminen edellyttää, että sillä on juurisolmu." +msgstr "" +"Skenen tallentaminen edellyttää, että sillä on juurisolmu. Voit lisätä " +"juurisolmun Skene-telakasta." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2914,9 +2931,8 @@ msgid "Orphan Resource Explorer..." msgstr "Irrallisten resurssien hallinta..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Nimetä projekti" +msgstr "Lataa uudelleen nykyinen projekti" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3076,22 +3092,20 @@ msgid "Help" msgstr "Ohje" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Avaa dokumentaatio" +msgstr "Online-dokumentaatio" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Kysymykset & vastaukset" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Raportoi bugi" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Aseta arvo" +msgstr "Ehdota ominaisuutta" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3102,9 +3116,8 @@ msgid "Community" msgstr "Yhteisö" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Tietoja" +msgstr "Tietoja Godotista" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3156,10 +3169,6 @@ msgid "Save & Restart" msgstr "Tallenna & käynnistä uudelleen" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Pyörii kun editorin ikkuna päivittyy." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Päivitä jatkuvasti" @@ -3200,14 +3209,12 @@ msgid "Manage Templates" msgstr "Hallinnoi malleja" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" msgstr "Asenna tiedostosta" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Valitse lähdemesh:" +msgstr "Valitse android-lähdetiedosto" #: editor/editor_node.cpp msgid "" @@ -3290,9 +3297,8 @@ msgid "Select" msgstr "Valitse" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Valitse nykyinen kansio" +msgstr "Valitse nykyinen" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3327,9 +3333,8 @@ msgid "No sub-resources found." msgstr "Aliresursseja ei löydetty." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Aliresursseja ei löydetty." +msgstr "Avaa aliresurssien luettelo." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3356,14 +3361,12 @@ msgid "Update" msgstr "Päivitä" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Versio:" +msgstr "Versio" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Tekijät" +msgstr "Tekijä" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3376,14 +3379,12 @@ msgid "Measure:" msgstr "Mittaa:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Kuvaruudun aika (sek)" +msgstr "Kuvaruudun aika (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Keskimääräinen aika (sek)" +msgstr "Keskimääräinen aika (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3410,6 +3411,13 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Sisältävä: Sisältää muissa tämän funktion kutsumissa funktioissa kuluneen " +"ajan.\n" +"Käytä tätä löytääksesi pullonkaulat.\n" +"\n" +"Itse: Lasketaan ainoastaan funktiossa itsessään kulunut aika, eikä muissa " +"tuon funktion kutsumissa funktioissa käytettyä aikaa.\n" +"Käytä tätä löytääksesi yksittäiset optimointia vaativat funktiot." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3532,7 +3540,6 @@ msgid "Paste" msgstr "Liitä" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Muunna muotoon %s" @@ -3625,31 +3632,27 @@ msgstr "Noudetaan peilipalvelimia, hetkinen..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Aloitetaan lataus..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Virhe pyydettäessä osoitetta:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." msgstr "Yhdistetään peilipalvelimeen..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Palvelinta ei löytynyt:" +msgstr "Pyydetyn osoitteen selvitys ei onnistunut." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Isäntään yhdistäminen epäonnistui:" +msgstr "Peilipalvelimeen yhdistäminen epäonnistui." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Ei vastausta isännältä:" +msgstr "Ei vastausta peilipalvelimelta." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3657,18 +3660,16 @@ msgid "Request failed." msgstr "Pyyntö epäonnistui." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Pyyntö epäonnistui, liikaa uudelleenohjauksia" +msgstr "Pyyntö päätyi uudelleenohjaussilmukkaan." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Pyyntö epäonnistui." +msgstr "Pyyntö epäonnistui:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Lataaminen valmis; puretaan malleja..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3695,7 +3696,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Paras saatavilla oleva peilipalvelin" #: editor/export_template_manager.cpp msgid "" @@ -3748,9 +3749,8 @@ msgid "SSL Handshake Error" msgstr "Virhe SSL kättelyssä" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Vientimallien zip-tiedostoa ei voitu avata." +msgstr "Vientimallien tiedostoa ei voida avata." #: editor/export_template_manager.cpp #, fuzzy @@ -3801,9 +3801,8 @@ msgid "Export templates are installed and ready to be used." msgstr "" #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Avaa tiedosto" +msgstr "Avaa kansio" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." @@ -3819,33 +3818,42 @@ msgid "Uninstall templates for the current version." msgstr "Laskurin alkuarvo" #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Latausvirhe" +msgstr "Lataa sijannista:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Suorita selaimessa" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopioi virhe" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Lataa ja asenna" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Lataa ja asenna mallit nykyiselle versiolle parhaasta mahdollisesta " +"peilipalvelimesta." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "Viralliset vientimallit eivät ole saatavilla kehityskäännöksille." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" msgstr "Asenna tiedostosta" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Tuo mallit ZIP-tiedostosta" +msgstr "Asenna mallit paikallisesta tiedostosta." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3853,19 +3861,16 @@ msgid "Cancel" msgstr "Peruuta" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Vientimallien zip-tiedostoa ei voitu avata." +msgstr "Keskeytä mallien lataus." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Asennetut versiot:" +msgstr "Muut asennetut versiot:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Poista asennus" +msgstr "Poista malli" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3880,6 +3885,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Mallien lataus jatkuu.\n" +"Saatat kokea lyhyitä editorin jähmettymisiä niiden tullessa valmiiksi." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4026,35 +4033,32 @@ msgid "Collapse All" msgstr "Tiivistä kaikki" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Etsi tiedostoista" +msgstr "Lajittele tiedostot" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Lajittele nimen mukaan (nouseva)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Lajittele nimen mukaan (laskeva)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Lajittele tyypin mukaan (nouseva)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Lajittele tyypin mukaan (laskeva)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Viimeksi muutettu" +msgstr "Lajittele viimeksi muokatun mukaan" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Viimeksi muutettu" +msgstr "Lajittele ensiksi muokatun mukaan" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4378,14 +4382,12 @@ msgid "Failed to load resource." msgstr "Resurssin lataaminen epäonnistui." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Ominaisuudet" +msgstr "Kopioi ominaisuudet" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Ominaisuudet" +msgstr "Liitä ominaisuudet" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4410,9 +4412,8 @@ msgid "Save As..." msgstr "Tallenna nimellä..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Ei löytynyt resurssipolusta." +msgstr "Ylimääräiset resurssivalinnat." #: editor/inspector_dock.cpp #, fuzzy @@ -5437,7 +5438,7 @@ msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Etsi assetteja (poislukien mallit, projektit ja demot)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5481,7 +5482,7 @@ msgstr "Assettien zip-tiedosto" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Äänen esikuuntelun toisto/keskeytys" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5741,13 +5742,12 @@ msgstr "Muuta ankkureita" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Pelikameran ohitus\n" -"Ohittaa pelikameran editorin näyttöruutukameralla." +"Projektikameran ohitus\n" +"Ohittaa käynnissä olevan projektin kameran editorin näyttöruutukameralla." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5756,6 +5756,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Projektikameran ohitus\n" +"Mikään projekti ei ole käynnissä. Aja projekti editorista käyttääksesi tätä " +"ominaisuutta." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -8692,6 +8695,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Poista kaikki" @@ -8722,6 +8731,12 @@ msgid "Remove All StyleBox Items" msgstr "Poista kaikki" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Lisää luokka" @@ -12384,6 +12399,16 @@ msgstr "Muuta sylinterimuodon korkeutta" msgid "Change Ray Shape Length" msgstr "Vaihda säteen muodon pituutta" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Aseta käyräpisteen sijainti" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Aseta käyräpisteen sijainti" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Muuta sylinterin sädettä" @@ -14163,6 +14188,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Tämä kappale sivuutetaan, kunnes asetat meshin." @@ -15490,9 +15551,6 @@ msgstr "Vakioita ei voi muokata." #~ msgid "I see..." #~ msgstr "Ymmärrän..." -#~ msgid "Can't open '%s'." -#~ msgstr "Ei voida avata tiedostoa '%s'." - #~ msgid "Ugh" #~ msgstr "Äh" diff --git a/editor/translations/fil.po b/editor/translations/fil.po index aacdf9c442..e53b7bb1a7 100644 --- a/editor/translations/fil.po +++ b/editor/translations/fil.po @@ -345,6 +345,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -369,10 +370,26 @@ msgstr "Lumikha" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Pagulit ng Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -928,7 +945,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2222,6 +2239,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3002,10 +3030,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Tuloy-tuloy" @@ -3615,6 +3639,14 @@ msgid "Download from:" msgstr "Kumpleto ang pag-Download." #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8303,6 +8335,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8329,6 +8367,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11749,6 +11793,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13350,6 +13402,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/fr.po b/editor/translations/fr.po index 9be7d406dd..e6e2c9021e 100644 --- a/editor/translations/fr.po +++ b/editor/translations/fr.po @@ -42,7 +42,7 @@ # Xananax <xananax@yelostudio.com>, 2017-2018. # Perrier Mathis <mathis.perrier73@gmail.com>, 2018. # Ewan Lehnebach <ewan.lehnebach@gmail.com>, 2018. -# Hugo Locurcio <hugo.locurcio@hugo.pro>, 2018, 2019, 2020. +# Hugo Locurcio <hugo.locurcio@hugo.pro>, 2018, 2019, 2020, 2021. # Grigore Antoniuc <grisa181@gmail.com>, 2018. # x2f <x.defoy@gmail.com>, 2018. # LittleWhite <lw.demoscene@googlemail.com>, 2018. @@ -81,12 +81,14 @@ # ASTRALE <jules.cercy@etu.univ-lyon1.fr>, 2021. # Julien Vanelian <julienvanelian@hotmail.com>, 2021. # Clément Topy <topy72.mine@gmail.com>, 2021. +# Cold <coldragon78@gmail.com>, 2021. +# Blackiris <divjvc@free.fr>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-16 05:47+0000\n" -"Last-Translator: Pierre Caye <pierrecaye@laposte.net>\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" +"Last-Translator: Blackiris <divjvc@free.fr>\n" "Language-Team: French <https://hosted.weblate.org/projects/godot-engine/" "godot/fr/>\n" "Language: fr\n" @@ -94,7 +96,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -416,6 +418,7 @@ msgstr "Modifier le mode de boucle d’animation" msgid "Remove Anim Track" msgstr "Supprimer la piste d’animation" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Créer une NOUVELLE piste pour %s et insérer une clé ?" @@ -440,11 +443,29 @@ msgstr "Créer" msgid "Anim Insert" msgstr "Insérer une animation" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Mode d'aimantation (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Un AnimationPlayer ne peut s’animer lui-même, seulement les autres lecteurs." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Il n'y a pas de propriété « %s »." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Créer et insérer une animation" @@ -660,9 +681,8 @@ msgid "Go to Previous Step" msgstr "Aller à l'étape précédente" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Réinitialiser" +msgstr "Appliquer la réinitialisation" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -681,9 +701,8 @@ msgid "Use Bezier Curves" msgstr "Utiliser les courbes de Bézier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Coller pistes" +msgstr "Créer des pistes RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -1008,7 +1027,6 @@ msgid "Edit..." msgstr "Édition..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Aller à la méthode" @@ -1028,9 +1046,9 @@ msgstr "Créer un nouveau %s" msgid "No results for \"%s\"." msgstr "Aucun résultats pour \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Pas de description disponible pour %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1130,18 +1148,16 @@ msgid "Owners Of:" msgstr "Propriétaires de :" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Supprimer les fichiers sélectionnés du projet ? (annulation impossible)\n" -"Vous pouvez retrouver les fichiers supprimés dans la corbeille du système " -"pour les restaurer." +"Supprimer les fichiers sélectionnés du projet ? (Annulation impossible.)\n" +"En fonction de la configuration de votre système, les fichiers seront soient " +"déplacés vers la corbeille du système, soit supprimés définitivement." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1151,9 +1167,9 @@ msgid "" msgstr "" "Les fichiers qui vont être supprimés sont utilisés par d'autres ressources " "pour leur fonctionnement.\n" -"Les supprimer tout de même ? (annulation impossible)\n" -"Vous pouvez retrouver les fichiers supprimés dans la corbeille du système " -"pour les restaurer." +"Les supprimer tout de même ? (Annulation impossible.)\n" +"En fonction de la configuration de votre système, les fichiers seront soient " +"déplacés vers la corbeille du système, soit supprimés définitivement." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1323,42 +1339,41 @@ msgid "Licenses" msgstr "Licences" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "" -"Erreur lors de l'ouverture du fichier package (il n'est pas au format ZIP)." +"Erreur lors de l'ouverture du fichier d'asset « %s » (il n'est pas au format " +"ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (existe déjà )" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Contenus de l'asset « %s » - %d fichier(s) sont en conflit avec votre " +"projet :" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Contenus de l'asset « %s » - Pas de fichiers en conflit avec votre projet :" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Décompression des assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "L'extraction des fichiers suivants depuis le paquetage a échoué :" +msgstr "L'extraction des fichiers suivants depuis l'asset « %s » a échoué :" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Et %s fichiers supplémentaires." +msgstr "(et %s fichiers supplémentaires)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Paquetage installé avec succès !" +msgstr "Asset « %s » installé avec succès !" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1370,9 +1385,8 @@ msgid "Install" msgstr "Installer" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Installeur de paquetage" +msgstr "Installateur d'asset" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1435,7 +1449,6 @@ msgid "Bypass" msgstr "Contourner" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Options de bus" @@ -1605,28 +1618,29 @@ msgid "Can't add autoload:" msgstr "Impossible d'ajouter le chargement automatique :" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Le fichier n'existe pas." +msgstr "%s est un chemin invalide. Le fichier n'existe pas." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." msgstr "" +"%s est un chemin invalide. Il n'est pas dans le chemin des ressources " +"(res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" -msgstr "Ajouter le chargement automatique" +msgstr "Ajouter chargement automatique" #: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp #: editor/editor_plugin_settings.cpp #: editor/plugins/animation_tree_editor_plugin.cpp #: editor/script_create_dialog.cpp scene/gui/file_dialog.cpp msgid "Path:" -msgstr "Chemin :" +msgstr "Chemin :" #: editor/editor_autoload_settings.cpp msgid "Node Name:" -msgstr "Nom de nÅ“ud :" +msgstr "Nom de nÅ“ud :" #: editor/editor_autoload_settings.cpp editor/editor_help_search.cpp #: editor/editor_plugin_settings.cpp editor/editor_profiler.cpp @@ -1635,9 +1649,8 @@ msgid "Name" msgstr "Nom" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Renommer la variable" +msgstr "Variable globale" #: editor/editor_data.cpp msgid "Paste Params" @@ -1811,48 +1824,55 @@ msgstr "Dock d'importation" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permet de visualiser et modifier des scènes 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"Permet de modifier des scripts à l'aide de l'éditeur de script intégré." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Fournit un accès à l'Asset Library." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permet de visualiser la hiérarchie des nÅ“uds dans le dock Scène." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Permet de travailler avec les signaux et groupes d'un nÅ“ud sélectionné dans " +"le dock Scène." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permet de naviguer le système de fichiers local à l'aide d'un dock dédié." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Permet de configurer les paramètres d'importation pour des ressources " +"individuelles. Nécessite le dock Système de fichiers pour fonctionner." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Actuel)" +msgstr "(actuel)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(aucun)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"Supprimer le profil actuellement sélectionné « %s » ? Cette suppression ne " +"peut être annulée." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1884,19 +1904,16 @@ msgid "Enable Contextual Editor" msgstr "Ouvrir l'éditeur contextuel" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Propriétés :" +msgstr "Propriétés de la classe :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "Fonctionnalités" +msgstr "Fonctionnalités principales :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Classes activées :" +msgstr "NÅ“uds et classes :" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1915,23 +1932,20 @@ msgid "Error saving profile to path: '%s'." msgstr "Erreur lors de l'enregistrement du profil au chemin : « %s »." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Réinitialiser" +msgstr "Réinitialiser aux valeurs par défaut" #: editor/editor_feature_profile.cpp msgid "Current Profile:" msgstr "Profil actuel :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Effacer le profil" +msgstr "Créer un profil" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Supprimer la tuile" +msgstr "Supprimer le profil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1951,18 +1965,18 @@ msgid "Export" msgstr "Exporter" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Profil actuel :" +msgstr "Configurer le profil sélectionné :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Options de classe :" +msgstr "Options additionnelles :" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Créer ou importer un profil pour modifier les classes et propriétés " +"disponibles." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1989,9 +2003,8 @@ msgid "Select Current Folder" msgstr "Sélectionner le dossier courant" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Le fichier existe, l'écraser ?" +msgstr "Le fichier existe, l'écraser ?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2384,6 +2397,17 @@ msgid "New Window" msgstr "Nouvelle Fenêtre" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Tourne lorsque la fenêtre de l'éditeur est redessinée." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Les ressources importées ne peuvent pas être sauvegardées." @@ -2620,13 +2644,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"La scène actuelle n'a pas de nÅ“ud racine, mais %d ressources externes " +"modifiées ont tout de même été enregistrées." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Un nÅ“ud racine est nécessaire pour sauvegarder la scène." +msgstr "" +"Un nÅ“ud racine est nécessaire pour enregistrer la scène. Vous pouvez ajouter " +"un nÅ“ud racine en utilisant le dock Scène." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3018,9 +3045,8 @@ msgid "Orphan Resource Explorer..." msgstr "Explorateur de ressources orphelines..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Renommer le projet" +msgstr "Recharger le projet actuel" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3183,13 +3209,12 @@ msgid "Help" msgstr "Aide" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Ouvrir la documentation" +msgstr "Documentation en ligne" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Questions et réponses" #: editor/editor_node.cpp msgid "Report a Bug" @@ -3197,7 +3222,7 @@ msgstr "Signaler un bug" #: editor/editor_node.cpp msgid "Suggest a Feature" -msgstr "" +msgstr "Suggérer une fonctionnalité" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3208,9 +3233,8 @@ msgid "Community" msgstr "Communauté" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "À propos" +msgstr "À propos de Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3262,10 +3286,6 @@ msgid "Save & Restart" msgstr "Enregistrer et redémarrer" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Tourne lorsque la fenêtre de l'éditeur est redessinée." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Mettre à jour en continu" @@ -3308,14 +3328,12 @@ msgid "Manage Templates" msgstr "Gérer les modèles" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Installer depuis fichier" +msgstr "Installer depuis un fichier" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Sélectionnez un maillage source :" +msgstr "Sélectionner un fichier de sources Android" #: editor/editor_node.cpp msgid "" @@ -3398,9 +3416,8 @@ msgid "Select" msgstr "Sélectionner" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Sélectionner le dossier courant" +msgstr "Sélectionner le dossier actuel" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3435,9 +3452,8 @@ msgid "No sub-resources found." msgstr "Aucune sous-ressource n'a été trouvée." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Ouvrir une liste de sous-ressources." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3464,14 +3480,12 @@ msgid "Update" msgstr "Mettre à jour" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Version :" +msgstr "Version" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Auteurs" +msgstr "Auteur" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3481,25 +3495,23 @@ msgstr "État" #: editor/editor_profiler.cpp msgid "Measure:" -msgstr "Mesure :" +msgstr "Mesurer :" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Temps par trame (seconde)" +msgstr "Temps par image (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Temps moyen (seconde)" +msgstr "Temps moyen (ms)" #: editor/editor_profiler.cpp msgid "Frame %" -msgstr "Trame %" +msgstr "Image %" #: editor/editor_profiler.cpp msgid "Physics Frame %" -msgstr "Trame physique %" +msgstr "Image physique %" #: editor/editor_profiler.cpp msgid "Inclusive" @@ -3518,10 +3530,17 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Inclusif : Inclut le temps passé dans les fonctions appelées par cette " +"fonction.\n" +"Utilisez ce mode pour repérer les goulots d'étranglement.\n" +"\n" +"Self : N'inclure que le temps passé dans la fonction elle-même, et non le " +"temps passé dans d'autres fonctions appelées par cette fonction.\n" +"Utilisez ce mode pour trouver des fonctions individuelles à optimiser." #: editor/editor_profiler.cpp msgid "Frame #:" -msgstr "Trame # :" +msgstr "Image # :" #: editor/editor_profiler.cpp msgid "Time" @@ -3640,9 +3659,8 @@ msgid "Paste" msgstr "Coller" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" -msgstr "Convertir en %s" +msgstr "Convertir vers %s" #: editor/editor_resource_picker.cpp editor/property_editor.cpp msgid "New %s" @@ -3690,11 +3708,10 @@ msgid "Did you forget the '_run' method?" msgstr "Avez-vous oublié la méthode « _run » ?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Maintenir Ctrl pour arrondir à l'entier. Maintenir Maj pour des changements " -"plus précis." +"Maintenir %s pour arrondir à l'entier près. Maintenir Maj. pour des " +"changements plus précis." #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -3714,49 +3731,43 @@ msgstr "Importer à partir d'un nÅ“ud :" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Ouvrir le dossier contenant ces modèles." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Désinstaller ces modèles." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "'%s' n'existe pas." +msgstr "Il n'y a pas de miroirs disponibles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Récupération des miroirs, veuillez patienter..." +msgstr "Récupération des miroirs…" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Démarrage du téléchargement…" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Erreur lors de la demande de l’URL :" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Connexion au Miroir..." +msgstr "Connexion au miroir…" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Impossible de résoudre le nom de l'hôte :" +msgstr "Impossible de résoudre l'adresse demandée." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Connexion à l'hôte impossible :" +msgstr "Impossible de se connecter au miroir." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Pas de réponse de l'hôte :" +msgstr "Pas de réponse du miroir." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3764,22 +3775,20 @@ msgid "Request failed." msgstr "La requête a échoué." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "La requête a échoué, trop de redirections" +msgstr "La requête a échoué car il y a eu trop de redirections." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "La requête a échoué." +msgstr "La requête a échoué :" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Téléchargement terminé, extraction des modèles…" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" -msgstr "Impossible de supprimer le fichier temporaire :" +msgstr "Impossible de supprimer le fichier temporaire :" #: editor/export_template_manager.cpp msgid "" @@ -3787,14 +3796,13 @@ msgid "" "The problematic templates archives can be found at '%s'." msgstr "" "L'installation des modèles a échoué.\n" -"Les archives des modèles problématiques se trouvent dans '%s'." +"Les archives des modèles problématiques se trouvent dans « %s »." #: editor/export_template_manager.cpp msgid "Error getting the list of mirrors." msgstr "Erreur lors du téléchargement de la liste des miroirs." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" "Erreur lors de la lecture de la liste JSON des miroirs. Merci de signaler ce " @@ -3802,7 +3810,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Meilleur miroir disponible" #: editor/export_template_manager.cpp msgid "" @@ -3855,25 +3863,20 @@ msgid "SSL Handshake Error" msgstr "Erreurs de la négociation SSL" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "" -"Impossible d'ouvrir le fichier ZIP contenant les modèles d'exportation." +msgstr "Impossible d'ouvrir le fichier contenant les modèles d'exportation." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Format de version.txt invalide dans les modèles : %s." +msgstr "Format de version.txt invalide dans le fichier de modèles : %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Aucun version.txt n'a été trouvé dans les modèles." +msgstr "Aucun fichier version.txt n'a été trouvé dans le fichier des modèles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Erreur lors de la création du chemin pour les modèles :" +msgstr "Erreur lors de la création du chemin pour l'extraction des modèles :" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3884,9 +3887,8 @@ msgid "Importing:" msgstr "Importation :" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Supprimer la version « %s » du modèle ?" +msgstr "Supprimer les modèles pour la version « %s » ?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3903,43 +3905,56 @@ msgstr "Version courante :" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Les modèles d'exportation sont manquants. Téléchargez-les ou installez-les " +"depuis un fichier." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Les modèles d'exportation sont installés et prêts à être utilisés." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Ouvrir le fichier" +msgstr "Ouvrir le dossier" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." msgstr "" +"Ouvrir le dossier contenant les modèles d'exportation pour la version " +"actuelle." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Désinstaller" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Valeur initiale pour le compteur" +msgstr "Désinstaller les modèles d'exportation pour la version actuelle." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Erreur de téléchargement" +msgstr "Télécharger depuis :" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Exécuter dans le navigateur" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copier l'erreur" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Télécharger et installer" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Télécharger et installer les modèles d'exportation pour la version actuelle " +"depuis le meilleur miroir disponible." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." @@ -3948,14 +3963,12 @@ msgstr "" "versions de développement." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Installer depuis fichier" +msgstr "Installer depuis un fichier" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Importer des modèles depuis un fichier ZIP" +msgstr "Installer des modèles d'exportation depuis un fichier local." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3963,24 +3976,20 @@ msgid "Cancel" msgstr "Annuler" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "" -"Impossible d'ouvrir le fichier ZIP contenant les modèles d'exportation." +msgstr "Annuler le téléchargement des modèles d'exportation." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Versions installées :" +msgstr "Autres versions installées :" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Désinstaller" +msgstr "Désinstaller le modèle" #: editor/export_template_manager.cpp msgid "Select Template File" -msgstr "Sélectionner le fichier de modèle" +msgstr "Sélectionner le fichier de modèles" #: editor/export_template_manager.cpp msgid "Godot Export Templates" @@ -3991,6 +4000,10 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Les modèles d'exportation vont continuer à être téléchargés en arrière-" +"plan.\n" +"Vous pourrez peut-être remarquer un court gel de l'éditeur lorsque le " +"téléchargement est terminé." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4138,35 +4151,32 @@ msgid "Collapse All" msgstr "Réduire tout" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Rechercher des fichiers" +msgstr "Trier les fichiers" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Trier par nom (ascendant)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Trier par nom (descendant)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Trier par type (ascendant)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Trier par type (descendant)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Dernière modification" +msgstr "Trier par date de modification (plus récent au moins récent)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Dernière modification" +msgstr "Trier par date de modification (moins récent au plus récent)" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4178,7 +4188,7 @@ msgstr "Renommer..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Mettre la boîte de recherche en surbrillance" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4488,14 +4498,12 @@ msgid "Failed to load resource." msgstr "Impossible de charger la ressource." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Propriétés" +msgstr "Copier les propriétés" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Propriétés" +msgstr "Coller les propriétés" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4520,23 +4528,20 @@ msgid "Save As..." msgstr "Enregistrer sous…" #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Pas dans le chemin de la ressource." +msgstr "Options de ressource additionnelles." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Modifier le Presse-papiers de la ressource" +msgstr "Modifier la ressource depuis le presse-papiers" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Copier la ressource" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Rendre intégré" +msgstr "Rendre la ressource intégrée" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4551,9 +4556,8 @@ msgid "History of recently edited objects." msgstr "Historique des objets récemment édités." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Ouvrir la documentation" +msgstr "Ouvrir la documentation pour cet objet." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4564,9 +4568,8 @@ msgid "Filter properties" msgstr "Filtrer les propriétés" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Propriétés de l'objet." +msgstr "Gérer les propriétés de l'objet." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4814,9 +4817,8 @@ msgid "Blend:" msgstr "Mélange :" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Paramètre modifié" +msgstr "Paramètre modifié :" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5550,11 +5552,11 @@ msgstr "All" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Rechercher modèles, projets et démos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Rechercher des assets (à l'exception des modèles, projets et démos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5598,7 +5600,7 @@ msgstr "Fichier ZIP de données" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Aperçu audio lecture/pause" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5859,13 +5861,12 @@ msgstr "Modifier les ancres" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Remplacement de la Caméra du Jeu\n" -"Remplace la caméra du jeu par la caméra de la fenêtre d'affichage de " +"Remplacement de la caméra du projet\n" +"Remplace la caméra du projet par la caméra de la fenêtre d'affichage de " "l'editeur." #: editor/plugins/canvas_item_editor_plugin.cpp @@ -5875,6 +5876,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Remplacement de la caméra du projet\n" +"Pas d'instance du projet en cours d'exécution. Lancez le projet depuis " +"l'éditeur afin d'utiliser cette fonctionnalité." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5942,31 +5946,27 @@ msgstr "Mode sélection" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Supprimer le nÅ“ud sélectionné ou la transition." +msgstr "Glisser : Tourner le nÅ“ud sélectionné autour du pivot." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Glisser : déplacer" +msgstr "Alt + Glisser : Déplacer le nÅ“ud sélectionné." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Supprimer le nÅ“ud sélectionné ou la transition." +msgstr "V : Définir la position du pivot pour le nÅ“ud sélectionné." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Afficher une liste de tous les objets à la position cliquée\n" -"(identique à Alt+Bouton droit en mode sélection)." +"Alt + Clic droit : Afficher une liste de tous les nÅ“uds à la position " +"cliquée, y compris les nÅ“uds verrouillés." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "Clic droit : Ajouter un nÅ“ud à la position cliquée." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6204,14 +6204,12 @@ msgid "Clear Pose" msgstr "Vider la pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Ajouter un nÅ“ud" +msgstr "Ajouter un nÅ“ud ici" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instancier scène(s)" +msgstr "Instancer une scène ici" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6227,49 +6225,43 @@ msgstr "Vue panoramique" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Zoomer à 3.125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Zoomer à 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Zoomer à 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Dézoomer" +msgstr "Zoomer à 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Dézoomer" +msgstr "Zoomer à 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Dézoomer" +msgstr "Zoomer à 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Dézoomer" +msgstr "Zoomer à 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Dézoomer" +msgstr "Zoomer à 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Dézoomer" +msgstr "Zoomer à 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Zoomer à 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6516,9 +6508,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Impossible de créer une forme de collision convexe unique." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Créer une forme convexe unique" +msgstr "Créer une forme convexe simplifiée" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6557,9 +6548,8 @@ msgid "No mesh to debug." msgstr "Aucun maillage à déboguer." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "Le modèle n'a pas d'UV dans cette couche" +msgstr "Le maillage n'a pas d'UV dans la couche %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6628,9 +6618,8 @@ msgstr "" "collisions." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Créer une seule collision convexe sÅ“ur" +msgstr "Créer une collision sÅ“ur convexe simplifiée" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6638,20 +6627,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Créé une forme de collision complexe simplifiée.\n" +"Cela est similaire à une forme de collision, mais peut résulter en une " +"géométrie plus simple mais moins précise." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Créer plusieurs collisions convexes sÅ“urs" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" -"Crée une forme de collision basée sur les polygones.\n" -"Il s'agit d'une performance à mi-chemin entre les deux options ci-dessus." +"Crée une forme de collision basée sur des polygones.\n" +"Il s'agit d'une performance à mi-chemin entre une forme unique de collision " +"convexe et une collision basée sur des polygones." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7301,24 +7293,20 @@ msgid "ResourcePreloader" msgstr "ResourcePreloader" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Retourner horizontalement" +msgstr "Retourner les Portals" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "Compte de Points Générés :" +msgstr "Générer des points Room" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "Compte de Points Générés :" +msgstr "Générer des points" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Retourner horizontalement" +msgstr "Retourner le Portal" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7882,20 +7870,17 @@ msgid "None" msgstr "Aucun" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Mode rotation" +msgstr "Rotation" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Translater :" +msgstr "Translation" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Échelle :" +msgstr "Mode mise à l'échelle" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7918,52 +7903,44 @@ msgid "Animation Key Inserted." msgstr "Clé d'animation insérée." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Tangage (latéral)" +msgstr "Tangage :" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Azimuth :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Taille : " +msgstr "Taille :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Objets dessinés" +msgstr "Objets dessinés :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Modifications de materiau" +msgstr "Changements de matériau :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Modification de shader" +msgstr "Changements de shader :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Modifications de surface" +msgstr "Changements de surface :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Appels de graphes" +msgstr "Appels de dessin :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Vertex" +msgstr "Sommets :" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS : %d (%s ms)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8120,9 +8097,8 @@ msgid "Freelook Slow Modifier" msgstr "Ralentissement de la vue libre" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Changer la taille d'une caméra" +msgstr "Activer la prévisualisation de la caméra" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8145,9 +8121,8 @@ msgstr "" "performance en jeu." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Convertir en %s" +msgstr "Convertir les salles" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8169,7 +8144,6 @@ msgstr "" "(« rayon x »)." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Aligner les nÅ“uds avec le sol" @@ -8187,7 +8161,7 @@ msgstr "Utiliser l’aimantation" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Convertit des salles pour l'occlusion culling à l'aide de portails." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8283,9 +8257,8 @@ msgid "View Grid" msgstr "Afficher la grille" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Paramètres de la vue" +msgstr "Afficher le Portal culling" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8599,230 +8572,208 @@ msgid "Step:" msgstr "Pas (s) :" #: editor/plugins/texture_region_editor_plugin.cpp -#, fuzzy msgid "Separation:" -msgstr "Recensements :" +msgstr "Séparation :" #: editor/plugins/texture_region_editor_plugin.cpp msgid "TextureRegion" msgstr "RegionDeTexture" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Couleur" +msgstr "Couleurs" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Police" +msgstr "Polices" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Icône" +msgstr "Icônes" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Style" +msgstr "Styleboxes" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} couleur(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas de couleurs trouvées." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "Constantes" +msgstr "{num} constante(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Constante de couleur." +msgstr "Pas de constantes trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} police(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas de polices trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} icône(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas d'icônes trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} stylebox(es)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas de styleboxes trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} actuellement sélectionné(s)" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Rien n'a été sélectionné pour l'importation." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Importer un thème" +msgstr "Importation des items de thème" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Importation de l'item {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Quitter l'éditeur ?" +msgstr "Mise à jour de l'éditeur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Analyse" +msgstr "Finalisation" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Filtres :" +msgstr "Filtre :" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "Avec données" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Sélectionner un nÅ“ud" +msgstr "Sélectionner par type de données :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Sélectionnez un fractionnement à effacer." +msgstr "Sélectionner tous les items de couleur visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." msgstr "" +"Sélectionner tous les items de couleur visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "Désélectionner tous les items de couleur visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items de constantes visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." msgstr "" +"Sélectionner tous les items de constantes visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "Désélectionner tous les items de constantes visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items de police visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." msgstr "" +"Sélectionner tous les items de police visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "Désélectionner tous les items de police visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items d'icône visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items d'icône visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Désélectionner tous les items d'icône visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Sélectionner tous les items de stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." msgstr "" +"Sélectionner tous les items de stylebox visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "Désélectionner tous les items de stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Attention : Ajouter des données d'icônes peut augmenter considérablement la " +"taille de votre ressource Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Réduire tout" +msgstr "Réduire les types." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Développer tout" +msgstr "Développer les types." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Sélectionner le fichier de modèle" +msgstr "Sélectionner tous les items du Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Sélectionner des points" +msgstr "Sélectionner avec les données" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Sélectionne tous les items de thème avec les données d'item." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Tout sélectionner" +msgstr "Tout déselectionner" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "Déselectionne tous les items du Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Importer une scène" +msgstr "Importer la sélection" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8830,275 +8781,253 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"L'onglet Importer des items a des items sélectionnés. La sélection sera " +"perdue si vous fermez cette fenêtre.\n" +"Fermer tout de même ?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de couleur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Supprimer l'item" +msgstr "Renommer l'item" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de constante" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de police" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items d'icône" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de stylebox" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Ajouter des items de classe" +msgstr "Ajouter un item de couleur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Ajouter des items de classe" +msgstr "Ajouter un item de constante" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Ajouter un item" +msgstr "Ajouter un item de police" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Ajouter un item" +msgstr "Ajouter un item d'icône" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Ajouter tous les items" +msgstr "Ajouter un item de stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Supprimer des items de classe" +msgstr "Renommer l'item de couleur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Supprimer des items de classe" +msgstr "Renommer l'item de constante" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Renommer le nÅ“ud" +msgstr "Renommer l'item de police" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Renommer le nÅ“ud" +msgstr "Renommer l'item d'icône" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Supprimer l'élément sélectionné" +msgstr "Renommer l'item de stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "Fichier invalide, pas une disposition de bus audio." +msgstr "Fichier invalide, car ce n'est pas une ressource de type Theme." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." msgstr "" +"Fichier invalide, car il est identique à la ressource Theme actuellement en " +"cours de modification." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Gérer les modèles" +msgstr "Gérer les items de thème" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Élément modifiable" +msgstr "Modifier les items" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Type :" +msgstr "Types :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Type :" +msgstr "Ajouter un type :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Ajouter un item" +msgstr "Ajouter un item :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Ajouter tous les items" +msgstr "Ajouter un item de stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Supprimer l'item" +msgstr "Supprimer items :" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "Supprimer des items de classe" +msgstr "Supprimer les items de classe" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Supprimer des items de classe" +msgstr "Supprimer les items personnalisés" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Ajouter un item" +msgstr "Ajouter un item de thème" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Nom de nÅ“ud :" +msgstr "Ancien nom :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Importer un thème" +msgstr "Importer des items" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Par défaut" +msgstr "Thème par défaut" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Modifier le thème" +msgstr "Thème de l'éditeur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Supprimer une ressource" +msgstr "Sélectionnez une autre ressource Theme :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Importer un thème" +msgstr "Autre thème" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Configurer la grille" +msgstr "Confirmer le renommage d'item" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Renommer par lot" +msgstr "Annuler le renommage d'item" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Redéfinition" +msgstr "Remplacer l'item" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Désépingler cette StyleBox comme style principal." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Épingler cette StyleBox comme style principal. Modifier ses propriétés " +"mettra à jour les mêmes propriétés dans toutes les autres StyleBoxes " +"appartenant à ce type." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Type" +msgstr "Ajouter un type" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Ajouter un item" +msgstr "Ajouter un item de type" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Type de nÅ“ud" +msgstr "Types de nÅ“ud :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Charger défaut" +msgstr "Afficher par défaut" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Afficher les items de type par défaut à côté de ceux qui ont été surchargés." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Redéfinition" +msgstr "Tout surcharger" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Surcharge tous les items de type par défaut." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Thème" +msgstr "Thème :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Gérer les modèles d'exportation..." +msgstr "Gérer les items…" #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Ajoute, supprime, organise et importe des items de thème." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Aperçu" +msgstr "Ajouter un aperçu" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Aperçu de la mise à jour" +msgstr "Aperçu par défaut" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Sélectionnez un maillage source :" +msgstr "Sélectionner une scène UI :" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Active le sélectionneur de contrôle, qui permet de sélectionner visuellement " +"des types de contrôles à modifier." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" -msgstr "Activer / Désactiver bouton" +msgstr "Bouton à bascule (toggle)" #: editor/plugins/theme_editor_preview.cpp msgid "Disabled Button" @@ -9110,7 +9039,7 @@ msgstr "Item" #: editor/plugins/theme_editor_preview.cpp msgid "Disabled Item" -msgstr "Élément Désactivé" +msgstr "Item désactivé" #: editor/plugins/theme_editor_preview.cpp msgid "Check Item" @@ -9129,9 +9058,8 @@ msgid "Checked Radio Item" msgstr "Item radio coché" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Séparateur nommé." +msgstr "Séparateur nommé" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9184,19 +9112,22 @@ msgstr "Possède,Plusieurs,Options" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Chemin invalide. La ressource PackedScene a probablement été déplacée ou " +"supprimée." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"Ressource PackedScene invalide. La scène doit avoir un nÅ“ud de type Control " +"à sa racine." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "Fichier invalide, pas une disposition de bus audio." +msgstr "Fichier invalide, pas une ressource PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Recharge la scène pour refléter son état le plus actuel." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10605,9 +10536,8 @@ msgid "VisualShader" msgstr "VisualShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Modifier la propriété visuelle" +msgstr "Modifier la propriété visuelle :" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10733,9 +10663,8 @@ msgid "Script" msgstr "Script" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Mode d'exportation des scripts :" +msgstr "Mode d'exportation GDScript :" #: editor/project_export.cpp msgid "Text" @@ -10743,21 +10672,20 @@ msgstr "Texte" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Bytecode compilé (chargement plus rapide)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Chiffré (fournir clé ci-dessous)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Clé de chiffrement invalide (doit comporter 64 caractères)" +msgstr "" +"Clé de chiffrement invalide (doit comporter 64 caractères hexadécimaux)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Clé de chiffrement des scripts (256 bits en hexadécimal) :" +msgstr "Clé de chiffrement GDScript (256 bits en hexadécimal) :" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10832,7 +10760,6 @@ msgid "Imported Project" msgstr "Projet importé" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Nom du projet invalide." @@ -11061,14 +10988,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Voulez-vous vraiment lancer %d projets à la fois ?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Sélectionner appareil depuis la liste" +msgstr "Retirer %d projets de la liste ?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Sélectionner appareil depuis la liste" +msgstr "Retirer ce projet de la liste ?" #: editor/project_manager.cpp msgid "" @@ -11102,9 +11027,8 @@ msgid "Project Manager" msgstr "Gestionnaire de projets" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Projets" +msgstr "Projets locaux" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11115,23 +11039,20 @@ msgid "Last Modified" msgstr "Dernière modification" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "Exporter le projet" +msgstr "Modifier le projet" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Renommer le projet" +msgstr "Lancer le projet" #: editor/project_manager.cpp msgid "Scan" msgstr "Scanner" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Projets" +msgstr "Scanner des projets" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11142,14 +11063,12 @@ msgid "New Project" msgstr "Nouveau projet" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Projet importé" +msgstr "Importer un projet" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Renommer le projet" +msgstr "Retirer le projet" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11160,9 +11079,8 @@ msgid "About" msgstr "À propos" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Bibliothèque d'assets" +msgstr "Projets de l'Asset Library" #: editor/project_manager.cpp msgid "Restart Now" @@ -11174,7 +11092,7 @@ msgstr "Supprimer tout" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Supprimer les contenus du projet également (pas d'annulation !)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11189,12 +11107,10 @@ msgstr "" "Voulez-vous explorer des exemples de projets officiels dans l'Asset Library ?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Filtrer les propriétés" +msgstr "Filtrer parmi les projets" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " @@ -11211,7 +11127,7 @@ msgstr "Touche " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Touche physique" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11259,7 +11175,7 @@ msgstr "Périphérique" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (physique)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11402,23 +11318,23 @@ msgid "Override for Feature" msgstr "Écrasement d'un paramètre, dédié à un tag de fonctionnalité" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Ajouter une traduction" +msgstr "Ajouter %d traductions" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Supprimer la traduction" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Réaffectation (remap) des ressources ; Ajouter une réaffectation" +msgstr "" +"Réaffectation (remap) des ressources par traduction : Ajouter %d chemin(s)" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Réaffectation (remap) des ressources ; Ajouter une réaffectation" +msgstr "" +"Réaffectation (remap) des ressources par traduction : Ajouter %d " +"réaffectation(s)" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11450,7 +11366,7 @@ msgstr "Général" #: editor/project_settings_editor.cpp msgid "Override For..." -msgstr "Surcharge pour…" +msgstr "Surcharger pour…" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "The editor must be restarted for changes to take effect." @@ -11862,12 +11778,16 @@ msgstr "Supprimer le noeud \"%s\" ?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Pour sauvegarder la branche en tant que scène, il faut qu'une scène soit " +"ouverte dans l'éditeur." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Pour sauvegarder la branche en tant que scène, il faut sélectionner " +"seulement un nÅ“ud, mais vous avez sélectionné %d nÅ“uds." #: editor/scene_tree_dock.cpp msgid "" @@ -11876,6 +11796,12 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Impossible de sauvegarder la branche du nÅ“ud racine comme une scène " +"instanciée.\n" +"Pour créer une copie modifiable de la scène actuelle, dupliquez-la à l'aide " +"du menu contextuel du dock Système de fichiers\n" +"ou créez une scène héritée en utilisant Scène > Nouvelle scène héritée... à " +"la place." #: editor/scene_tree_dock.cpp msgid "" @@ -11883,6 +11809,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Impossible de sauvegarder la branche d'une scène déjà instanciée.\n" +"Pour créer une variation d'une scène, vous pouvez créer une scène héritée " +"basée sur la scène instanciée en utilisant Scène > Nouvelle scène héritée... " +"à la place." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12291,6 +12221,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Avertissement : Il n'est généralement pas souhaitable que le nom du script " +"soit le même que celui d'un type intégré." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12362,7 +12294,7 @@ msgstr "Copier l'erreur" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Ouvrir les sources C++ sur GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12541,6 +12473,16 @@ msgstr "Changer la hauteur de la forme du cylindre" msgid "Change Ray Shape Length" msgstr "Changer la longueur d'une forme en rayon" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Définir la position du point de la courbe" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Définir la position du point de la courbe" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Changer le rayon du cylindre" @@ -12656,12 +12598,11 @@ msgstr "L'objet ne peut fournir une longueur." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp #, fuzzy msgid "Export Mesh GLTF2" -msgstr "Exporter une bibliothèque de maillages" +msgstr "Exporter le Maillage GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "Exporter..." +msgstr "Exporter en GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12704,9 +12645,8 @@ msgid "GridMap Paint" msgstr "Peinture GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Remplissage de la sélection de GridMap" +msgstr "Sélection de la GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12960,14 +12900,12 @@ msgid "Add Output Port" msgstr "Ajouter un port de sortie" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Changer le type" +msgstr "Changer le Type de Port" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Changer le nom du port d'entrée" +msgstr "Changer le Nom du Port" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13353,38 +13291,33 @@ msgid "Select device from the list" msgstr "Sélectionner appareil depuis la liste" #: platform/android/export/export.cpp +#, fuzzy msgid "Running on %s" -msgstr "" +msgstr "En cours d'exécution sur %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "Tout exporter" +msgstr "Exportation de l'APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Désinstaller" +msgstr "Désinstallation..." #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Chargement en cours, veuillez patienter..." +msgstr "Installation sur l'appareil, veuillez patienter..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Impossible de démarrer le sous-processus !" +msgstr "Impossible d'installer sur l'appareil : %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "Lancement du script personnalisé…" +msgstr "En cours d'exécution sur l'appareil..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Impossible de créer le dossier." +msgstr "Impossible d'exécuter sur l'appareil." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13509,46 +13442,49 @@ msgstr "" "est activée." #: platform/android/export/export.cpp +#, fuzzy msgid "" "'apksigner' could not be found.\n" "Please check the command is available in the Android SDK build-tools " "directory.\n" "The resulting %s is unsigned." msgstr "" +"Impossible de trouver 'apksigner'.\n" +"Veuillez vérifier que la commande est disponible dans le dossier build-tools " +"du SDK Android.\n" +"Le paquet sortant %s est non signé." #: platform/android/export/export.cpp +#, fuzzy msgid "Signing debug %s..." -msgstr "" +msgstr "Signature du debug %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Analyse des fichiers en cours,\n" -"Veuillez patienter..." +msgstr "Signature de la version %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Impossible d'ouvrir le modèle pour exportation :" +msgstr "Impossible de trouver le keystore, impossible d'exporter." #: platform/android/export/export.cpp +#, fuzzy msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "'apksigner' a terminé avec l'erreur #%d" #: platform/android/export/export.cpp #, fuzzy msgid "Verifying %s..." -msgstr "Ajout de %s..." +msgstr "Vérification de %s..." #: platform/android/export/export.cpp +#, fuzzy msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "La vérification de %s avec 'apksigner' a échoué." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "Tout exporter" +msgstr "Exportation vers Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13570,7 +13506,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Format d'export non supporté !\n" #: platform/android/export/export.cpp msgid "" @@ -13594,20 +13530,20 @@ msgstr "" "Veuillez réinstaller la version d'Android depuis le menu 'Projet'." #: platform/android/export/export.cpp +#, fuzzy msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Impossible d'écraser les fichiers res://android/build/res/*.xml avec le nom " +"du projet" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "" -"Impossible de modifier le fichier project.godot dans le chemin du projet." +msgstr "Impossible d'exporter les fichiers du projet vers le projet gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Impossible d'écrire le fichier :" +msgstr "Impossible d'écrire le fichier du paquet d'expansion !" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13638,37 +13574,42 @@ msgstr "" #: platform/android/export/export.cpp #, fuzzy msgid "Package not found: %s" -msgstr "Animation introuvable : « %s »" +msgstr "Paquet introuvable : « %s »" #: platform/android/export/export.cpp #, fuzzy msgid "Creating APK..." -msgstr "Création des contours..." +msgstr "Création du fichier APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Impossible d'ouvrir le modèle pour exportation :" +msgstr "" +"Impossible de trouver le modèle de l'APK à exporter :\n" +"%s" #: platform/android/export/export.cpp +#, fuzzy msgid "" "Missing libraries in the export template for the selected architectures: " "%s.\n" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Bibliothèques manquantes dans le modèle d'export pour les architectures " +"sélectionnées : %s.\n" +"Veuillez construire un modèle avec toutes les bibliothèques requises, ou " +"désélectionner les architectures manquantes dans le préréglage de l'export." #: platform/android/export/export.cpp #, fuzzy msgid "Adding files..." -msgstr "Ajout de %s..." +msgstr "Ajout de fichiers..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Impossible d'écrire le fichier :" +msgstr "Impossible d'exporter les fichiers du projet" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13676,7 +13617,7 @@ msgstr "Alignement de l'APK…" #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Impossible de décompresser l'APK temporaire non aligné." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13723,9 +13664,8 @@ msgid "Could not write file:" msgstr "Impossible d'écrire le fichier :" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Impossible d'écrire le fichier :" +msgstr "Impossible de lire le fichier :" #: platform/javascript/export/export.cpp #, fuzzy @@ -13733,14 +13673,12 @@ msgid "Could not read HTML shell:" msgstr "Impossible de lire le shell HTML personnalisé :" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Impossible de créer le dossier." +msgstr "Impossible de créer le répertoire du serveur HTTP :" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Erreur d'enregistrement de la scène." +msgstr "Erreur de démarrage du serveur HTTP :" #: platform/osx/export/export.cpp #, fuzzy @@ -13748,8 +13686,9 @@ msgid "Invalid bundle identifier:" msgstr "Identifiant invalide :" #: platform/osx/export/export.cpp +#, fuzzy msgid "Notarization: code signing required." -msgstr "" +msgstr "Certification : signature du code requise." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." @@ -14206,11 +14145,16 @@ msgstr "" "A la place utilisez une BakedLightMap." #: scene/3d/gi_probe.cpp +#, fuzzy msgid "" "The GIProbe Compress property has been deprecated due to known bugs and no " "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"La propriété GIProbe Compress a été déclarée comme obsolète à cause de bugs " +"connus et n'a plus aucun effet.\n" +"Pour supprimer cette avertissement, désactivez la propriété Compress du " +"GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14304,15 +14248,15 @@ msgstr "Node A et Node B doivent être des PhysicsBody différents" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Le RoomManager ne doit pas être enfant ou grand-enfant d'un Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Un Room ne doit pas être enfant ou petit-enfant d'un Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Un RoomGroup ne doit pas être enfant ou petit-enfant d'un Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14362,6 +14306,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Ce corps sera ignoré jusqu'à ce que vous définissiez un maillage." @@ -15125,9 +15105,6 @@ msgstr "Les constantes ne peuvent être modifiées." #~ msgid "Local Coords" #~ msgstr "Coordonnées locales" -#~ msgid "Snap Mode (%s)" -#~ msgstr "Mode d'aimantation (%s)" - #~ msgid "Tool Select" #~ msgstr "Outil sélection" diff --git a/editor/translations/ga.po b/editor/translations/ga.po index 98fe774878..872463b1a9 100644 --- a/editor/translations/ga.po +++ b/editor/translations/ga.po @@ -337,6 +337,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -361,10 +362,26 @@ msgstr "Cruthaigh" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "CrannBeochan" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -920,7 +937,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2214,6 +2231,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2994,10 +3022,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3602,6 +3626,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8288,6 +8320,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8313,6 +8351,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11730,6 +11774,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13330,6 +13382,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/gl.po b/editor/translations/gl.po index 9b97e45e8a..054b62690d 100644 --- a/editor/translations/gl.po +++ b/editor/translations/gl.po @@ -6,18 +6,19 @@ # Andy Barcia <andybarcia4@gmail.com>, 2021. # PokeGalaico <abloodyfreaks@gmail.com>, 2021. # Kkai <kaieltroll@gmail.com>, 2021. +# davidrogel <david.rogel.pernas@icloud.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2021-04-26 22:32+0000\n" -"Last-Translator: Kkai <kaieltroll@gmail.com>\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" +"Last-Translator: davidrogel <david.rogel.pernas@icloud.com>\n" "Language-Team: Galician <https://hosted.weblate.org/projects/godot-engine/" "godot/gl/>\n" "Language: gl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -339,6 +340,7 @@ msgstr "Cambiar Modo de Bucle da Animación" msgid "Remove Anim Track" msgstr "Eliminar Pista de Animación" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Crear nova pista para %s e engadir chave?" @@ -363,10 +365,27 @@ msgstr "Crear" msgid "Anim Insert" msgstr "Engadir Animación" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animación" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Un AnimationPlayer non pode animarse a si mesmo, só a outros players." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Non existe a propiedade '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crear e Engadir Animación" @@ -943,9 +962,9 @@ msgstr "Crear Novo %s" msgid "No results for \"%s\"." msgstr "Non houbo resultado para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Non hai descrición dispoñible para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1140,7 +1159,7 @@ msgstr "Moitas grazas de parte da comunidade de Godot!" #: editor/editor_about.cpp editor/editor_node.cpp editor/project_manager.cpp msgid "Click to copy." -msgstr "" +msgstr "Clic para copiar." #: editor/editor_about.cpp msgid "Godot Engine contributors" @@ -1724,7 +1743,7 @@ msgstr "Importación" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite ver e editar escenas 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." @@ -2293,6 +2312,17 @@ msgid "New Window" msgstr "Nova Xanela" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Xira cando o editor actualiza a pantalla." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Os recursos importados non se poden gardar." @@ -3161,10 +3191,6 @@ msgid "Save & Restart" msgstr "Gardar e Reinicar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Xira cando o editor actualiza a pantalla." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizar de Maneira Continua" @@ -3797,6 +3823,15 @@ msgid "Download from:" msgstr "Erro na Descarga" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Abrir no Explorador de Arquivos" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8606,6 +8641,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Eliminar Tódolos Marcapáxinas" @@ -8636,6 +8677,12 @@ msgid "Remove All StyleBox Items" msgstr "Eliminar Tódolos Marcapáxinas" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Engadir Elemento" @@ -12185,6 +12232,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13860,6 +13915,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este corpo será ignorado ata que se lle sea asignado unha malla." diff --git a/editor/translations/he.po b/editor/translations/he.po index bbe0ebbe08..d0a09565de 100644 --- a/editor/translations/he.po +++ b/editor/translations/he.po @@ -355,6 +355,7 @@ msgstr "×©×™× ×•×™ מצב לול×ת ×”× ×¤×©×”" msgid "Remove Anim Track" msgstr "מחיקת רצועת ×”× ×¤×©×”" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "×”×× ×œ×™×¦×•×¨ רצועה חדשה ל%s ×•×œ×”×›× ×™×¡ מפתח?" @@ -379,10 +380,28 @@ msgstr "יצירה" msgid "Anim Insert" msgstr "הוסף ×”× ×¤×©×”" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "מצב הצמדה (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "×”× ×¤×©×”" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "× ×’×Ÿ ×”×”× ×¤×©×•×ª ×œ× ×™×›×•×œ ×œ×”× ×¤×™×© ×ת עצמו, רק ×©×—×§× ×™× ×חרי×." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "×œ× ×§×™×™× ×ž×פיין ‚%s’." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "יצירה והוספה של ×”× ×¤×©×”" @@ -960,7 +979,7 @@ msgstr "יצירת %s חדש" msgid "No results for \"%s\"." msgstr "×ין תוצ×ות עבור \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2309,6 +2328,17 @@ msgid "New Window" msgstr "חלון חדש" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "מסתובב ×›×שר חלון העורך מצויר מחדש." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "מש××‘×™× ×ž×™×•×‘××™× ×œ× × ×©×ž×¨×•." @@ -3143,10 +3173,6 @@ msgid "Save & Restart" msgstr "שמירה והפעלה מחדש" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "מסתובב ×›×שר חלון העורך מצויר מחדש." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "עדכון רציף" @@ -3789,6 +3815,16 @@ msgid "Download from:" msgstr "שגי×ת הורדה" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "הפעלה בדפדפן" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "שגי×ת העתקה" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8747,6 +8783,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "הסרת כל × ×§×•×“×•×ª העצירה" @@ -8777,6 +8819,12 @@ msgid "Remove All StyleBox Items" msgstr "הסרת כל × ×§×•×“×•×ª העצירה" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "מועדפי×:" @@ -12388,6 +12436,14 @@ msgstr "×©×™× ×•×™ גובה לצורת גליל" msgid "Change Ray Shape Length" msgstr "×©×™× ×•×™ ×ורך לצורת קרן" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "×©×™× ×•×™ רדיוס גליל" @@ -14113,6 +14169,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "תהיה התעלמות מגוף ×–×” עד שתקבע רשת." @@ -14643,9 +14735,6 @@ msgstr "××™ ×פשר ×œ×©× ×•×ª קבועי×." #~ msgid "Local Coords" #~ msgstr "× ×§×•×“×•×ª ציון מקומיות" -#~ msgid "Snap Mode (%s)" -#~ msgstr "מצב הצמדה (%s)" - #~ msgid "Project List" #~ msgstr "רשימת המיזמי×" diff --git a/editor/translations/hi.po b/editor/translations/hi.po index a4e7d4ae72..916e6fd01d 100644 --- a/editor/translations/hi.po +++ b/editor/translations/hi.po @@ -348,6 +348,7 @@ msgstr "à¤à¤¨à¤¿à¤®à¥‡à¤¶à¤¨ लूप मोड बदलें" msgid "Remove Anim Track" msgstr "अनीम टà¥à¤°à¥ˆà¤• निकालें" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "% à¤à¤¸ के लिठनया टà¥à¤°à¥ˆà¤• बनाà¤à¤‚ और कà¥à¤‚जी डालें?" @@ -372,10 +373,27 @@ msgstr "बनाना" msgid "Anim Insert" msgstr "अनीम डालें" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "कारà¥à¤¯à¥‹à¤‚:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "à¤à¤¨à¤¿à¤®à¥‡à¤¶à¤¨à¤ªà¥à¤²à¥‡à¤¯à¤° खà¥à¤¦ को चेतन नहीं कर सकता, केवल अनà¥à¤¯ खिलाड़ी।" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "पà¥à¤°à¥‹à¤ªà¤°à¥à¤Ÿà¥€" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "अनीम बनाà¤à¤‚ और डालें" @@ -947,7 +965,7 @@ msgstr "नया%s बनाà¤à¤‚" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2289,6 +2307,17 @@ msgid "New Window" msgstr "नया विंडो" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "जब संपादक खिड़की फिर से खींचता है तो सà¥à¤ªà¤¿à¤¨ करता है।" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "इंपोरà¥à¤Ÿà¥‡à¤¡ रेसोरà¥à¤¸à¥‡à¤¸ सेव नहीं कर सकते." @@ -3130,10 +3159,6 @@ msgid "Save & Restart" msgstr "सहेजें और पà¥à¤¨à¤ƒ आरंठकरें" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "जब संपादक खिड़की फिर से खींचता है तो सà¥à¤ªà¤¿à¤¨ करता है।" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "लगातार अपडेट करें" @@ -3785,6 +3810,15 @@ msgid "Download from:" msgstr "डाउनलोड" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "फ़ाइल मैनेजर में खोलिये" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8561,6 +8595,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "आइटम निकालें" @@ -8591,6 +8631,12 @@ msgid "Remove All StyleBox Items" msgstr "आइटम निकालें" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "पसंदीदा में जोड़ें" @@ -12124,6 +12170,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13771,6 +13825,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/hr.po b/editor/translations/hr.po index ea48ef0246..37d517cba0 100644 --- a/editor/translations/hr.po +++ b/editor/translations/hr.po @@ -340,6 +340,7 @@ msgstr "Promijeni NaÄin Ponavljanja Animacije" msgid "Remove Anim Track" msgstr "Ukloni Stazu Animacije" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Stvori NOVU stazu za %s i umetni kljuÄ?" @@ -364,10 +365,26 @@ msgstr "Stvori" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacija" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Animator ne može animirati sebe, samo druge animatore." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Stvori & Umetni" @@ -927,7 +944,7 @@ msgstr "Napravi novi %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2240,6 +2257,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3021,10 +3049,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Kontinuirano ažuriraj" @@ -3635,6 +3659,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Otvori u Inspektoru" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8337,6 +8370,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8364,6 +8403,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11805,6 +11850,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13413,6 +13466,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/hu.po b/editor/translations/hu.po index 3af983ff18..c822f5bd53 100644 --- a/editor/translations/hu.po +++ b/editor/translations/hu.po @@ -351,6 +351,7 @@ msgstr "Animáció Összefűzés Módjának Változtatása" msgid "Remove Anim Track" msgstr "Animáció Sáv EltávolÃtása" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Létrehoz ÚJ sávot a következÅ‘höz: %s, és beszúrja a kulcsot?" @@ -375,12 +376,29 @@ msgstr "Létrehozás" msgid "Anim Insert" msgstr "Animáció - Beszúrás" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animáció" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer nem tudja önmagát animálni, csak más AnimationPlayer " "elemeket." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Tulajdonság" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animáció - Létrehozás és Beillesztés" @@ -959,7 +977,7 @@ msgstr "Új %s létrehozása" msgid "No results for \"%s\"." msgstr "Nincs találat a következÅ‘re: \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2309,6 +2327,17 @@ msgid "New Window" msgstr "Új ablak" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Pörög, amikor a szerkesztÅ‘ablak újrarajzolódik." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Az importált erÅ‘források nem menthetÅ‘k." @@ -3178,10 +3207,6 @@ msgid "Save & Restart" msgstr "Mentés és újraindÃtás" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Pörög, amikor a szerkesztÅ‘ablak újrarajzolódik." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Folyamatos frissÃtés" @@ -3825,6 +3850,16 @@ msgid "Download from:" msgstr "Letöltési Hiba" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Megnyitás a FájlkezelÅ‘ben" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Hiba Másolása" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8609,6 +8644,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Összes könyvjelzÅ‘ eltávolÃtása" @@ -8639,6 +8680,12 @@ msgid "Remove All StyleBox Items" msgstr "Összes könyvjelzÅ‘ eltávolÃtása" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Elem Hozzáadása" @@ -12104,6 +12151,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Görbe Pont PozÃció BeállÃtása" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Görbe Pont PozÃció BeállÃtása" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13734,6 +13791,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/id.po b/editor/translations/id.po index ccc5865b85..3426bd0962 100644 --- a/editor/translations/id.po +++ b/editor/translations/id.po @@ -368,6 +368,7 @@ msgstr "Ubah Mode Perulangan Animasi" msgid "Remove Anim Track" msgstr "Hapus Trek Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Buat track BARU untuk %s dan masukkan tombol?" @@ -392,11 +393,29 @@ msgstr "Buat" msgid "Anim Insert" msgstr "Sisipkan Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Tidak dapat membuka '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasi" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer tidak bisa menganimasikan diri sendiri, gunakan pemutar lain." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Tidak ada properti '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Buat & Sisipkan Anim" @@ -972,7 +991,7 @@ msgstr "Buat %s baru" msgid "No results for \"%s\"." msgstr "Tidak ada hasil untuk \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2322,6 +2341,17 @@ msgid "New Window" msgstr "Jendela Baru" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Putar ketika jendela editor digambar ulang." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Resource yang diimpor tidak dapat disimpan." @@ -3180,10 +3210,6 @@ msgid "Save & Restart" msgstr "Simpan & Mulai Ulang" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Putar ketika jendela editor digambar ulang." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Perbarui Terus-menerus" @@ -3845,6 +3871,16 @@ msgid "Download from:" msgstr "Unduhan Gagal" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Jalankan di Peramban" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Salin Galat" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8712,6 +8748,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Hapus Semua Item" @@ -8742,6 +8784,12 @@ msgid "Remove All StyleBox Items" msgstr "Hapus Semua Item" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Tambah Item Kelas" @@ -12412,6 +12460,16 @@ msgstr "Ubah Tinggi Bentuk Silinder" msgid "Change Ray Shape Length" msgstr "Ubah Panjang Shape Ray" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Atur Posisi Titik Kurva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Atur Posisi Titik Kurva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Ubah Radius Silinder" @@ -14186,6 +14244,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Body ini akan diabaikan hingga Anda mengatur mesh-nya." @@ -15242,9 +15336,6 @@ msgstr "Konstanta tidak dapat dimodifikasi." #~ msgid "I see..." #~ msgstr "Mengerti..." -#~ msgid "Can't open '%s'." -#~ msgstr "Tidak dapat membuka '%s'." - #~ msgid "Ugh" #~ msgstr "Duh" diff --git a/editor/translations/is.po b/editor/translations/is.po index 916be97fb4..e536b0a8f6 100644 --- a/editor/translations/is.po +++ b/editor/translations/is.po @@ -360,6 +360,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "Fjarlægja Anim track" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -384,10 +385,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Stillið breyting á:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -954,7 +971,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2253,6 +2270,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3036,10 +3064,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Samfellt" @@ -3648,6 +3672,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Opna Verkefna Stjóra?" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8376,6 +8409,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8403,6 +8442,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11873,6 +11918,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13487,6 +13540,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/it.po b/editor/translations/it.po index cec4cf1beb..c3aa84d4b6 100644 --- a/editor/translations/it.po +++ b/editor/translations/it.po @@ -37,7 +37,7 @@ # Stefano Merazzi <asso99@hotmail.com>, 2019. # Sinapse X <sinapsex13@gmail.com>, 2019. # Micila Micillotto <micillotto@gmail.com>, 2019, 2020, 2021. -# Mirko Soppelsa <miknsop@gmail.com>, 2019, 2020. +# Mirko Soppelsa <miknsop@gmail.com>, 2019, 2020, 2021. # No <kingofwizards.kw7@gmail.com>, 2019. # StarFang208 <polaritymanx@yahoo.it>, 2019. # Katia Piazza <gydey@ridiculousglitch.com>, 2019. @@ -64,8 +64,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-26 14:18+0000\n" -"Last-Translator: Riteo Siuga <riteo@posteo.net>\n" +"PO-Revision-Date: 2021-08-10 21:39+0000\n" +"Last-Translator: Mirko <miknsop@gmail.com>\n" "Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/" "godot/it/>\n" "Language: it\n" @@ -73,7 +73,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -394,6 +394,7 @@ msgstr "Cambia la modalità del ciclo di un'animazione" msgid "Remove Anim Track" msgstr "Rimuovi una traccia d'animazione" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Creare una NUOVA traccia per %s e inserire la chiave?" @@ -418,10 +419,28 @@ msgstr "Crea" msgid "Anim Insert" msgstr "Inserisci un'animazione" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Impossibile aprire '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animazione" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer non può animare se stesso, solo altri nodi." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Non esiste nessuna proprietà \"%s\"." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crea un'animazione e inserisci un fotogramma chiave" @@ -1005,7 +1024,7 @@ msgstr "Crea un nuovo %s" msgid "No results for \"%s\"." msgstr "Nessun risultato per \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2372,6 +2391,17 @@ msgid "New Window" msgstr "Nuova Finestra" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira quando la finestra dell'editor viene ridisegnata." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Le risorse importate non possono essere salvate." @@ -3248,10 +3278,6 @@ msgid "Save & Restart" msgstr "Salva e riavvia" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira quando la finestra dell'editor viene ridisegnata." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Aggiorna continuamente" @@ -3499,7 +3525,6 @@ msgid "Inclusive" msgstr "Inclusivo" #: editor/editor_profiler.cpp -#, fuzzy msgid "Self" msgstr "Se stesso" @@ -3930,6 +3955,16 @@ msgid "Download from:" msgstr "Errore durante il download" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Esegui nel Browser" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copia Errore" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -7908,9 +7943,8 @@ msgid "Translate" msgstr "Trasla:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Scala:" +msgstr "Scala" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -8855,6 +8889,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Rimuovi tutti gli elementi" @@ -8885,6 +8925,12 @@ msgid "Remove All StyleBox Items" msgstr "Rimuovi tutti gli elementi" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Aggiungi Elementi di Classe" @@ -12574,6 +12620,16 @@ msgstr "Modifica Altezza di Forma del Cilindro" msgid "Change Ray Shape Length" msgstr "Cambia lunghezza Ray Shape" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Imposta Posizione Punto Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Imposta Posizione Punto Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Modifica Raggio del Cilindro" @@ -13569,7 +13625,7 @@ msgstr "" #: platform/android/export/export.cpp #, fuzzy msgid "Exporting for Android" -msgstr "Esportando Tutto" +msgstr "Esportazione per Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -14377,6 +14433,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Questo corpo verrà ignorato fino a quando non imposterai una mesh." @@ -15641,9 +15733,6 @@ msgstr "Le constanti non possono essere modificate." #~ msgid "I see..." #~ msgstr "Capisco..." -#~ msgid "Can't open '%s'." -#~ msgstr "Impossibile aprire '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/ja.po b/editor/translations/ja.po index 83e544e5b5..3ee6d0b49d 100644 --- a/editor/translations/ja.po +++ b/editor/translations/ja.po @@ -15,7 +15,7 @@ # Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@gmail.com>, 2017-2018. # yu tang <0011solo@gmail.com>, 2018. # zukkun <zukkun@gmail.com>, 2018. -# sugusan <sugusan.development@gmail.com>, 2018, 2019. +# sugusan <sugusan.development@gmail.com>, 2018, 2019, 2021. # Nathan Lovato <nathan.lovato.art@gmail.com>, 2018. # nyanode <akaruooyagi@yahoo.co.jp>, 2018. # nitenook <admin@alterbaum.net>, 2018, 2019, 2020, 2021. @@ -37,8 +37,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" -"Last-Translator: nitenook <admin@alterbaum.net>\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" +"Last-Translator: sugusan <sugusan.development@gmail.com>\n" "Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/" "godot/ja/>\n" "Language: ja\n" @@ -46,7 +46,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -367,6 +367,7 @@ msgstr "アニメーションã®ãƒ«ãƒ¼ãƒ—モードを変更" msgid "Remove Anim Track" msgstr "アニメーショントラックを除去" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s ã®æ–°è¦ãƒˆãƒ©ãƒƒã‚¯ã‚’作æˆã—ã€ã‚ーを挿入ã—ã¾ã™ã‹ï¼Ÿ" @@ -391,12 +392,30 @@ msgstr "作æˆ" msgid "Anim Insert" msgstr "アニメーション挿入" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'..'を処ç†ã§ãã¾ã›ã‚“" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "アニメーション" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "アニメーションプレイヤーã¯ä»–ã®ãƒ—レイヤーã ã‘をアニメーション化ã™ã‚‹ã“ã¨ã¯ã§ã" "ã¾ã›ã‚“。" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "プãƒãƒ‘ティ '%s' ã¯å˜åœ¨ã—ã¾ã›ã‚“。" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "アニメーションã®ä½œæˆã¨æŒ¿å…¥" @@ -972,9 +991,9 @@ msgstr "%s ã‚’æ–°è¦ä½œæˆ" msgid "No results for \"%s\"." msgstr "\"%s\" ã®çµæžœã¯ã‚りã¾ã›ã‚“。" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "%s ã«ã¤ã„ã¦ã®èª¬æ˜Žã¯ã‚りã¾ã›ã‚“。" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1166,7 +1185,7 @@ msgstr "Godot コミュニティより感è¬ã‚’ï¼" #: editor/editor_about.cpp editor/editor_node.cpp editor/project_manager.cpp msgid "Click to copy." -msgstr "" +msgstr "クリックã—ã¦ã‚³ãƒ”ーã—ã¾ã™ã€‚" #: editor/editor_about.cpp msgid "Godot Engine contributors" @@ -2325,6 +2344,17 @@ msgid "New Window" msgstr "æ–°è¦ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "エディタ ウィンドウã®å†æç”»æ™‚ã«ã‚¹ãƒ”ンã—ã¾ã™ã€‚" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "インãƒãƒ¼ãƒˆã—ãŸãƒªã‚½ãƒ¼ã‚¹ã¯ä¿å˜ã§ãã¾ã›ã‚“。" @@ -3185,10 +3215,6 @@ msgid "Save & Restart" msgstr "ä¿å˜ã—ã¦å†èµ·å‹•" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "エディタ ウィンドウã®å†æç”»æ™‚ã«ã‚¹ãƒ”ンã—ã¾ã™ã€‚" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "ç¶™ç¶šçš„ã«æ›´æ–°" @@ -3634,11 +3660,11 @@ msgstr "ノードã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆ:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "ã“れらã®ãƒ†ãƒ³ãƒ—レートãŒã‚るフォルダを開ãã¾ã™ã€‚" #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "ã“れらã®ãƒ†ãƒ³ãƒ—レートをアンインストールã—ã¾ã™ã€‚" #: editor/export_template_manager.cpp #, fuzzy @@ -3652,7 +3678,7 @@ msgstr "ミラーをå–å¾—ã—ã¦ã„ã¾ã™ã€‚ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„... #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "ダウンãƒãƒ¼ãƒ‰ã‚’é–‹å§‹ã—ã¦ã„ã¾ã™..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" @@ -3695,7 +3721,7 @@ msgstr "リクエストã¯å¤±æ•—ã—ã¾ã—ãŸã€‚" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "ダウンãƒãƒ¼ãƒ‰ãŒå®Œäº†ã—ã¾ã—ãŸã€‚テンプレートを展開ã—ã¦ã„ã¾ã™..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3820,10 +3846,12 @@ msgstr "ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"エクスãƒãƒ¼ãƒˆ テンプレートãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。ダウンãƒãƒ¼ãƒ‰ã™ã‚‹ã‹ãƒ•ァイルã‹ã‚‰ã‚¤ãƒ³" +"ストールã—ã¦ãã ã•ã„。" #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "エクスãƒãƒ¼ãƒˆ テンプレートã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ãŠã‚Šã€åˆ©ç”¨ã§ãã¾ã™ã€‚" #: editor/export_template_manager.cpp #, fuzzy @@ -3849,8 +3877,18 @@ msgid "Download from:" msgstr "ダウンãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ブラウザã§å®Ÿè¡Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "エラーをコピー" + +#: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "ダウンãƒãƒ¼ãƒ‰ã—ã¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«" #: editor/export_template_manager.cpp msgid "" @@ -4052,35 +4090,32 @@ msgid "Collapse All" msgstr "ã™ã¹ã¦æŠ˜ã‚ŠãŸãŸã‚€" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "ファイル検索" +msgstr "ファイルã®ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "åå‰ (æ˜‡é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "åå‰ (é™é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "種類 (æ˜‡é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "種類 (é™é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "最終更新" +msgstr "æ›´æ–°æ—¥æ™‚ãŒæ–°ã—ã„é †ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "最終更新" +msgstr "更新日時ãŒå¤ã„é †ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -8708,6 +8743,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ã™ã¹ã¦ã®ã‚¢ã‚¤ãƒ†ãƒ を除去" @@ -8738,6 +8779,12 @@ msgid "Remove All StyleBox Items" msgstr "ã™ã¹ã¦ã®ã‚¢ã‚¤ãƒ†ãƒ を除去" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "ã‚¯ãƒ©ã‚¹ã‚¢ã‚¤ãƒ†ãƒ è¿½åŠ " @@ -12390,6 +12437,16 @@ msgstr "円柱シェイプã®é«˜ã•を変更" msgid "Change Ray Shape Length" msgstr "レイシェイプã®é•·ã•を変更" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "カーブãƒã‚¤ãƒ³ãƒˆã®ä½ç½®ã‚’è¨å®š" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "カーブãƒã‚¤ãƒ³ãƒˆã®ä½ç½®ã‚’è¨å®š" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "円柱ã®åŠå¾„を変更" @@ -14160,6 +14217,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "ã“ã®ãƒœãƒ‡ã‚£ã¯ã€ãƒ¡ãƒƒã‚·ãƒ¥ã‚’è¨å®šã™ã‚‹ã¾ã§ç„¡è¦–ã•れã¾ã™ã€‚" @@ -15444,10 +15537,6 @@ msgstr "定数ã¯å¤‰æ›´ã§ãã¾ã›ã‚“。" #~ msgstr "ã‚ã‹ã£ãŸ..." #, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "'..'を処ç†ã§ãã¾ã›ã‚“" - -#, fuzzy #~ msgid "Ugh" #~ msgstr "ã†ã‡" diff --git a/editor/translations/ka.po b/editor/translations/ka.po index 144ef77158..7abc89b216 100644 --- a/editor/translations/ka.po +++ b/editor/translations/ka.po @@ -366,6 +366,7 @@ msgstr "áƒáƒœáƒ˜áƒ› ლუპის შეცვლáƒ" msgid "Remove Anim Track" msgstr "áƒáƒœáƒ˜áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ თრექის წáƒáƒ¨áƒšáƒ" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp #, fuzzy msgid "Create NEW track for %s and insert key?" @@ -391,6 +392,17 @@ msgstr "შექმნáƒ" msgid "Anim Insert" msgstr "áƒáƒœáƒ˜áƒ› ჩáƒáƒ§áƒ”ნებáƒ" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ფუნქციები:" + #: editor/animation_track_editor.cpp #, fuzzy msgid "AnimationPlayer can't animate itself, only other players." @@ -398,6 +410,12 @@ msgstr "" "áƒáƒœáƒ˜áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ გáƒáƒ›áƒ¨áƒ•ები ვერჩáƒáƒáƒ¢áƒáƒ ებს ცდებს სáƒáƒ™áƒ£áƒ—áƒáƒ თáƒáƒ•ზე, მხáƒáƒšáƒáƒ“ სხვრ" "მáƒáƒ—áƒáƒ›áƒáƒ¨áƒ”ებზე." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "áƒáƒ‘იექტზე დáƒáƒ™áƒ•ირვებáƒ" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "áƒáƒœáƒ˜áƒ› შექმნრ& ჩáƒáƒ§áƒ”ნებáƒ" @@ -988,7 +1006,7 @@ msgstr "áƒáƒ®áƒáƒšáƒ˜ %s შექმნáƒ" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2331,6 +2349,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3120,10 +3149,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "უწყვეტი" @@ -3741,6 +3766,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "გáƒáƒ®áƒ¡áƒœáƒ˜áƒšáƒ˜" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8578,6 +8612,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "სáƒáƒ§áƒ•áƒáƒ ლები:" @@ -8606,6 +8646,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "სáƒáƒ§áƒ•áƒáƒ ლები:" @@ -12141,6 +12187,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13772,6 +13826,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/km.po b/editor/translations/km.po index e18a778132..187307bc17 100644 --- a/editor/translations/km.po +++ b/editor/translations/km.po @@ -337,6 +337,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -361,10 +362,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -919,7 +935,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2210,6 +2226,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2989,10 +3016,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3597,6 +3620,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8277,6 +8308,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8301,6 +8338,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11708,6 +11751,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13302,6 +13353,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ko.po b/editor/translations/ko.po index eda096e4ce..1f24eb1b1d 100644 --- a/editor/translations/ko.po +++ b/editor/translations/ko.po @@ -27,7 +27,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-16 05:47+0000\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" "Last-Translator: Myeongjin Lee <aranet100@gmail.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/" "godot/ko/>\n" @@ -36,7 +36,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -358,6 +358,7 @@ msgstr "ì• ë‹ˆë©”ì´ì…˜ 루프 모드 바꾸기" msgid "Remove Anim Track" msgstr "ì• ë‹ˆë©”ì´ì…˜ 트랙 ì‚ì œ" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%sì„(를) 위해 새 íŠ¸ëž™ì„ ë§Œë“¤ê³ í‚¤ë¥¼ ì‚½ìž…í• ê¹Œìš”?" @@ -382,12 +383,30 @@ msgstr "만들기" msgid "Anim Insert" msgstr "ì• ë‹ˆë©”ì´ì…˜ 삽입" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'%s' 열수 ì—†ìŒ." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ì• ë‹ˆë©”ì´ì…˜" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer는 ìžì‹ ì´ ì•„ë‹Œ 다른 í”Œë ˆì´ì–´ì—ë§Œ ì• ë‹ˆë©”ì´ì…˜ì„ ë¶€ì—¬í• ìˆ˜ 있습니" "다." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "'%s' ì†ì„±ì´ 없습니다." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "ì• ë‹ˆë©”ì´ì…˜ 만들기 & 삽입" @@ -961,9 +980,9 @@ msgstr "새 %s 만들기" msgid "No results for \"%s\"." msgstr "\"%s\"ì— ëŒ€í•œ 결과가 없습니다." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "%sì˜ ì‚¬ìš© 가능한 ì„¤ëª…ì´ ì—†ìŠµë‹ˆë‹¤." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1263,11 +1282,11 @@ msgstr "%s (ì´ë¯¸ 존재함)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "ì• ì…‹ \"%s\"ì˜ ë‚´ìš© - íŒŒì¼ %d개가 프로ì 트와 ì¶©ëŒí•©ë‹ˆë‹¤:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "ì• ì…‹ \"%s\"ì˜ ë‚´ìš© - 프로ì 트와 ì¶©ëŒí•˜ëŠ” 파ì¼ì´ 없습니다:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" @@ -1279,9 +1298,8 @@ msgid "The following files failed extraction from asset \"%s\":" msgstr "ë‹¤ìŒ íŒŒì¼ì„ 패키지ì—서 ì¶”ì¶œí•˜ëŠ”ë° ì‹¤íŒ¨í•¨:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "외 %d ê°œì˜ íŒŒì¼." +msgstr "(ë° ë” ë§Žì€ íŒŒì¼ %sê°œ)" #: editor/editor_asset_installer.cpp #, fuzzy @@ -1531,13 +1549,12 @@ msgid "Can't add autoload:" msgstr "ì˜¤í† ë¡œë“œë¥¼ ì¶”ê°€í• ìˆ˜ ì—†ìŒ:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "파ì¼ì´ 존재하지 않습니다." +msgstr "%s는 ìž˜ëª»ëœ ê²½ë¡œìž…ë‹ˆë‹¤. 파ì¼ì´ 존재하지 않습니다." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s는 ìž˜ëª»ëœ ê²½ë¡œìž…ë‹ˆë‹¤. 리소스 경로(res://)ì— ìžˆì§€ 않습니다." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1814,9 +1831,8 @@ msgid "Class Properties:" msgstr "ì†ì„±:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "기능" +msgstr "주요 기능:" #: editor/editor_feature_profile.cpp #, fuzzy @@ -1876,14 +1892,12 @@ msgid "Export" msgstr "내보내기" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "현재 프로필:" +msgstr "ì„ íƒëœ 프로필 구성:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "í…ìŠ¤ì³ ì˜µì…˜" +msgstr "별ë„ì˜ ì˜µì…˜:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." @@ -2307,6 +2321,17 @@ msgid "New Window" msgstr "새 ì°½" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "편집기 ì°½ì— ë³€í™”ê°€ ìžˆì„ ë•Œë§ˆë‹¤ íšŒì „í•©ë‹ˆë‹¤." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "ê°€ì ¸ì˜¨ 리소스를 ì €ìž¥í• ìˆ˜ 없습니다." @@ -2448,9 +2473,9 @@ msgid "" "Please read the documentation relevant to importing scenes to better " "understand this workflow." msgstr "" -"ì´ ë¦¬ì†ŒìŠ¤ëŠ” ê°€ì ¸ì˜¨ ì”¬ì— ì†í•œ 리소스ì´ë¯€ë¡œ íŽ¸ì§‘í• ìˆ˜ 없습니다.\n" -"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ë¬¸ì„œë¥¼ ì½ì–´ì£¼" -"세요." +"`ì´ ë¦¬ì†ŒìŠ¤ëŠ” ê°€ì ¸ì˜¨ ì”¬ì— ì†í•œ 리소스ì´ë¯€ë¡œ íŽ¸ì§‘í• ìˆ˜ 없습니다.\n" +"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ì„¤ëª…ë¬¸ì„œë¥¼ ì½" +"어주세요." #: editor/editor_node.cpp msgid "" @@ -2477,8 +2502,8 @@ msgid "" msgstr "" "ì´ ì”¬ì€ ê°€ì ¸ì˜¨ 것ì´ë¯€ë¡œ 변경 사í•ì´ ìœ ì§€ë˜ì§€ 않습니다.\n" "ì´ ì”¬ì„ ì¸ìŠ¤í„´ìŠ¤í™”í•˜ê±°ë‚˜ ìƒì†í•˜ë©´ íŽ¸ì§‘í• ìˆ˜ 있습니다.\n" -"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ë¬¸ì„œë¥¼ ì½ì–´ì£¼" -"세요." +"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ì„¤ëª…ë¬¸ì„œë¥¼ ì½" +"어주세요." #: editor/editor_node.cpp msgid "" @@ -2487,7 +2512,7 @@ msgid "" "this workflow." msgstr "" "ì›ê²© ê°ì²´ëŠ” 변경사í•ì´ ì ìš©ë˜ì§€ 않습니다.\n" -"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 디버깅(Debugging)ê³¼ ê´€ë ¨ëœ ë¬¸ì„œë¥¼ ì½ì–´ì£¼ì„¸ìš”." +"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 디버깅(Debugging)ê³¼ ê´€ë ¨ëœ ì„¤ëª…ë¬¸ì„œë¥¼ ì½ì–´ì£¼ì„¸ìš”." #: editor/editor_node.cpp msgid "There is no defined scene to run." @@ -2568,7 +2593,7 @@ msgstr "ì´ ìž‘ì—…ì—는 ì„ íƒí•œ 노드가 필요합니다." #: editor/editor_node.cpp msgid "Current scene not saved. Open anyway?" -msgstr "현재 ì”¬ì„ ì €ìž¥í•˜ì§€ 않았습니다. ë¬´ì‹œí•˜ê³ ì—´ê¹Œìš”?" +msgstr "현재 ì”¬ì´ ì €ìž¥ë˜ì–´ 있지 않습니다. ë¬´ì‹œí•˜ê³ ì—¬ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Can't reload a scene that was never saved." @@ -2600,11 +2625,11 @@ msgstr "예" #: editor/editor_node.cpp msgid "Exit the editor?" -msgstr "편집기를 ëŒê¹Œìš”?" +msgstr "편집기를 ë‚˜ê°€ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Open Project Manager?" -msgstr "프로ì 트 ë§¤ë‹ˆì €ë¥¼ 열까요?" +msgstr "프로ì 트 ë§¤ë‹ˆì €ë¥¼ ì—¬ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Save & Quit" @@ -2612,11 +2637,11 @@ msgstr "ì €ìž¥ & 종료" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before quitting?" -msgstr "ë„기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í• ê¹Œìš”?" +msgstr "종료하기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before opening Project Manager?" -msgstr "프로ì 트 ë§¤ë‹ˆì €ë¥¼ 열기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í• ê¹Œìš”?" +msgstr "프로ì 트 매니터를 열기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "" @@ -2949,7 +2974,7 @@ msgid "" msgstr "" "ì´ ì˜µì…˜ì´ í™œì„±í™” ëœ ê²½ìš° ì› í´ë¦ ë°°í¬ë¥¼ 사용하면 ì‹¤í–‰ì¤‘ì¸ í”„ë¡œì 트를 디버깅 " "í• ìˆ˜ 있ë„ë¡ì´ ì»´í“¨í„°ì˜ IPì— ì—°ê²°ì„ ì‹œë„합니다.\n" -"ì´ ì˜µì…˜ì€ ì›ê²© 디버깅 (ì¼ë°˜ì 으로 ëª¨ë°”ì¼ ìž¥ì¹˜ 사용)ì— ì‚¬ìš©í•˜ê¸°ìœ„í•œ 것입니" +"ì´ ì˜µì…˜ì€ ì›ê²© 디버깅 (ì¼ë°˜ì 으로 ëª¨ë°”ì¼ ê¸°ê¸° 사용)ì— ì‚¬ìš©í•˜ê¸° 위한 것입니" "다.\n" "GDScript 디버거를 로컬ì—서 사용하기 위해 활성화 í• í•„ìš”ëŠ” 없습니다." @@ -3009,8 +3034,8 @@ msgid "" msgstr "" "ì´ ì„¤ì •ì´ í™œì„±í™”ëœ ê²½ìš°, 편집기ì—서 ì”¬ì„ ìˆ˜ì •í•˜ë©´ ì‹¤í–‰ì¤‘ì¸ í”„ë¡œì íŠ¸ì— ë°˜ì˜ë©" "니다.\n" -"ì›ê²©ìž¥ì¹˜ì—서 ì‚¬ìš©ì¤‘ì¸ ê²½ìš° ë„¤íŠ¸ì›Œí¬ íŒŒì¼ ì‹œìŠ¤í…œ ê¸°ëŠ¥ì„ í™œì„±í™”í•˜ë©´ ë”ìš± 효율ì " -"입니다." +"기기ì—서 ì›ê²©ìœ¼ë¡œ ì‚¬ìš©ì¤‘ì¸ ê²½ìš° ë„¤íŠ¸ì›Œí¬ íŒŒì¼ ì‹œìŠ¤í…œ ê¸°ëŠ¥ì„ í™œì„±í™”í•˜ë©´ ë”ìš± " +"효율ì 입니다." #: editor/editor_node.cpp msgid "Synchronize Script Changes" @@ -3081,9 +3106,8 @@ msgid "Help" msgstr "ë„움ë§" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "문서 열기" +msgstr "온ë¼ì¸ 설명문서" #: editor/editor_node.cpp msgid "Questions & Answers" @@ -3161,10 +3185,6 @@ msgid "Save & Restart" msgstr "ì €ìž¥ & 다시 시작" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "편집기 ì°½ì— ë³€í™”ê°€ ìžˆì„ ë•Œë§ˆë‹¤ íšŒì „í•©ë‹ˆë‹¤." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "ìƒì‹œ ì—…ë°ì´íЏ" @@ -3210,9 +3230,8 @@ msgid "Install from file" msgstr "파ì¼ì—서 설치" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "소스 메시를 ì„ íƒí•˜ì„¸ìš”:" +msgstr "Android 소스 íŒŒì¼ ì„ íƒ" #: editor/editor_node.cpp msgid "" @@ -3358,9 +3377,8 @@ msgid "Update" msgstr "ì—…ë°ì´íЏ" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "ë²„ì „:" +msgstr "ë²„ì „" #: editor/editor_plugin_settings.cpp #, fuzzy @@ -3609,7 +3627,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "ì´ í…œí”Œë¦¿ì„ ì œê±°í•©ë‹ˆë‹¤." #: editor/export_template_manager.cpp #, fuzzy @@ -3635,42 +3653,37 @@ msgid "Connecting to the mirror..." msgstr "ë¯¸ëŸ¬ì— ì—°ê²° 중..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "호스트 ì´ë¦„ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ:" +msgstr "ìš”ì²ëœ 주소를 í•´ê²°í• ìˆ˜ 없습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "í˜¸ìŠ¤íŠ¸ì— ì—°ê²°í• ìˆ˜ ì—†ìŒ:" +msgstr "ë¯¸ëŸ¬ì— ì—°ê²°í• ìˆ˜ 없습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "í˜¸ìŠ¤íŠ¸ì˜ ì‘답 ì—†ìŒ:" +msgstr "미러로부터 ì‘ë‹´ì´ ì—†ìŠµë‹ˆë‹¤." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Request failed." -msgstr "ìš”ì² ì‹¤íŒ¨í•¨." +msgstr "ìš”ì²ì— 실패했습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "ìš”ì² ì‹¤íŒ¨. 너무 ë§Žì€ ë¦¬ë‹¤ì´ë ‰íЏ" +msgstr "ìš”ì²ì´ ë¦¬ë””ë ‰ì…˜ 루프로 ë났습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "ìš”ì² ì‹¤íŒ¨í•¨." +msgstr "ìš”ì² ì‹¤íŒ¨ë¨:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "다운로드를 완료하여 í…œí”Œë¦¿ì„ ì••ì¶• í•´ì œ 중..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" -msgstr "임시 파ì¼ì„ ì €ìž¥í• ìˆ˜ ì—†ìŒ:" +msgstr "임시 파ì¼ì„ ì‚ì œí• ìˆ˜ ì—†ìŒ:" #: editor/export_template_manager.cpp msgid "" @@ -3691,7 +3704,7 @@ msgstr "미러 목ë¡ì˜ JSON 구문 ë¶„ì„ ì¤‘ 오류. ì´ ë¬¸ì œë¥¼ ì‹ ê³ í•´ì #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "최ìƒì˜ 사용 가능한 미러" #: editor/export_template_manager.cpp msgid "" @@ -3807,17 +3820,26 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Uninstall" -msgstr "ì‚ì œ" +msgstr "ì œê±°" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "ì¹´ìš´í„°ì˜ ì´ˆê¸° ê°’" +msgstr "현재 ë²„ì „ì„ ìœ„í•œ í…œí”Œë¦¿ì„ ì œê±°í•©ë‹ˆë‹¤." #: editor/export_template_manager.cpp #, fuzzy msgid "Download from:" -msgstr "다운로드 오류" +msgstr "ë‹¤ìŒ ìœ„ì¹˜ì—서 다운로드:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "브ë¼ìš°ì €ì—서 실행" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "복사 오류" #: editor/export_template_manager.cpp msgid "Download and Install" @@ -3839,9 +3861,8 @@ msgid "Install from File" msgstr "파ì¼ì—서 설치" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "ZIP 파ì¼ì—서 템플릿 ê°€ì ¸ì˜¤ê¸°" +msgstr "로컬 파ì¼ë¡œë¶€í„° í…œí”Œë¦¿ì„ ì„¤ì¹˜í•©ë‹ˆë‹¤." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3859,9 +3880,8 @@ msgid "Other Installed Versions:" msgstr "ì„¤ì¹˜ëœ ë²„ì „:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "ì‚ì œ" +msgstr "템플릿 ì œê±°" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -4401,9 +4421,8 @@ msgid "Save As..." msgstr "다른 ì´ë¦„으로 ì €ìž¥..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "리소스 ê²½ë¡œì— ì—†ìŠµë‹ˆë‹¤." +msgstr "별ë„ì˜ ë¦¬ì†ŒìŠ¤ 옵션." #: editor/inspector_dock.cpp #, fuzzy @@ -4432,13 +4451,12 @@ msgid "History of recently edited objects." msgstr "ìµœê·¼ì— íŽ¸ì§‘í•œ ê°ì²´ 기ë¡ìž…니다." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "문서 열기" +msgstr "ì´ ê°ì²´ë¥¼ 위한 설명문서를 엽니다." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" -msgstr "문서 열기" +msgstr "설명문서 열기" #: editor/inspector_dock.cpp msgid "Filter properties" @@ -4690,9 +4708,8 @@ msgid "Blend:" msgstr "혼합:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "매개변수 변경ë¨" +msgstr "매개변수 변경ë¨:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5526,7 +5543,7 @@ msgstr "미리 보기" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap" -msgstr "스냅 ì„¤ì •" +msgstr "스냅 구성" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Grid Offset:" @@ -5802,27 +5819,22 @@ msgstr "모드 ì„ íƒ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "ì„ íƒí•œ 노드나 ì „í™˜ì„ ì‚ì œí•©ë‹ˆë‹¤." +msgstr "드래그: 피벗 ì£¼ìœ„ì— ì„ íƒëœ 노드를 íšŒì „í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+드래그: ì´ë™" +msgstr "Alt+드래그: ì„ íƒëœ 노드를 ì´ë™í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "ì„ íƒí•œ 노드나 ì „í™˜ì„ ì‚ì œí•©ë‹ˆë‹¤." +msgstr "V: ì„ íƒëœ ë…¸ë“œì˜ í”¼ë²— 위치를 ì„¤ì •í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"í´ë¦í•œ ìœ„ì¹˜ì— ìžˆëŠ” ëª¨ë“ ê°ì²´ 목ë¡ì„ 보여줘요\n" -"(ì„ íƒ ëª¨ë“œì—서 Alt+ìš°í´ë¦ê³¼ ê°™ìŒ)." +"Alt+ìš°í´ë¦: í´ë¦ëœ ìœ„ì¹˜ì— ìžˆëŠ” ìž ê¸ˆì„ í¬í•¨í•œ ëª¨ë“ ë…¸ë“œì˜ ëª©ë¡ì„ ë³´ì—¬ì¤ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." @@ -5831,12 +5843,12 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Move Mode" -msgstr "ì´ë™ëª¨ë“œ" +msgstr "ì´ë™ 모드" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate Mode" -msgstr "íšŒì „ëª¨ë“œ" +msgstr "íšŒì „ 모드" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5866,7 +5878,7 @@ msgstr "ìž ëª¨ë“œ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Toggle smart snapping." -msgstr "스마트 스냅 í† ê¸€." +msgstr "스마트 ìŠ¤ëƒ…ì„ í† ê¸€í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Smart Snap" @@ -5874,7 +5886,7 @@ msgstr "스마트 스냅 사용" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Toggle grid snapping." -msgstr "ê²©ìž ìŠ¤ëƒ… í† ê¸€." +msgstr "ê²©ìž ìŠ¤ëƒ…ì„ í† ê¸€í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Grid Snap" @@ -5907,7 +5919,7 @@ msgstr "스마트 스냅" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Configure Snap..." -msgstr "스냅 ì„¤ì •..." +msgstr "스냅 구성..." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to Parent" @@ -6410,9 +6422,8 @@ msgid "No mesh to debug." msgstr "ë””ë²„ê·¸í• ë©”ì‹œê°€ 없습니다." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "ì´ ë ˆì´ì–´ì—서 모ë¸ì€ UVê°€ 없습니다" +msgstr "ë ˆì´ì–´ %dì—서 ë©”ì‹œì— UVê°€ 없습니다." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -7068,7 +7079,7 @@ msgstr "ê²©ìž ë³´ì´ê¸°" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Configure Grid:" -msgstr "ê²©ìž ì„¤ì •:" +msgstr "ê²©ìž êµ¬ì„±:" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid Offset X:" @@ -7146,14 +7157,12 @@ msgid "Flip Portals" msgstr "수í‰ìœ¼ë¡œ 뒤집기" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "ìƒì„±í•œ ì 개수:" +msgstr "ë°© ìƒì„±í•œ ì 개수" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "ìƒì„±í•œ ì 개수:" +msgstr "ìƒì„±í•œ ì 개수" #: editor/plugins/room_manager_editor_plugin.cpp #, fuzzy @@ -7402,11 +7411,11 @@ msgstr "온ë¼ì¸ 문서" #: editor/plugins/script_editor_plugin.cpp msgid "Open Godot online documentation." -msgstr "Godot 온ë¼ì¸ 문서를 엽니다." +msgstr "Godot 온ë¼ì¸ 설명문서를 엽니다." #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." -msgstr "참조 문서를 검색합니다." +msgstr "참조 설명문서를 검색합니다." #: editor/plugins/script_editor_plugin.cpp msgid "Go to previous edited document." @@ -7607,7 +7616,7 @@ msgstr "ì´ì „ ë¶ë§ˆí¬ë¡œ ì´ë™" #: editor/plugins/script_text_editor.cpp msgid "Remove All Bookmarks" -msgstr "ëª¨ë“ ë¶ë§ˆí¬ ì œê±°" +msgstr "ëª¨ë“ ë¶ë§ˆí¬ ì‚ì œ" #: editor/plugins/script_text_editor.cpp msgid "Go to Function..." @@ -7624,7 +7633,7 @@ msgstr "중단ì í† ê¸€" #: editor/plugins/script_text_editor.cpp msgid "Remove All Breakpoints" -msgstr "중단ì ëª¨ë‘ ì œê±°" +msgstr "ëª¨ë“ ì¤‘ë‹¨ì ì‚ì œ" #: editor/plugins/script_text_editor.cpp msgid "Go to Next Breakpoint" @@ -7721,20 +7730,17 @@ msgid "None" msgstr "ì—†ìŒ" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "주(State)" +msgstr "íšŒì „" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "ì´ë™:" +msgstr "ì´ë™" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "í¬ê¸°:" +msgstr "í¬ê¸°" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7757,48 +7763,40 @@ msgid "Animation Key Inserted." msgstr "ì• ë‹ˆë©”ì´ì…˜ 키를 삽입했습니다." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "피치" +msgstr "피치:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" msgstr "" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "í¬ê¸°: " +msgstr "í¬ê¸°:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "ê·¸ë ¤ì§„ ê°ì²´" +msgstr "ê·¸ë ¤ì§„ ê°ì²´:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "머티리얼 바꾸기" +msgstr "머티리얼 바꾸기:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "ì…°ì´ë” 바꾸기" +msgstr "ì…°ì´ë” 바꾸기:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "표면 바꾸기" +msgstr "표면 바꾸기:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "드로우 콜" +msgstr "드로우 콜:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "ì " +msgstr "ì •ì :" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" @@ -8459,71 +8457,63 @@ msgstr "ìŠ¤íƒ€ì¼ ë°•ìŠ¤" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num}색" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "하위 리소스를 ì°¾ì„ ìˆ˜ 없습니다." +msgstr "ìƒ‰ì„ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "ìƒìˆ˜" +msgstr "ìƒìˆ˜ {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "ìƒ‰ìƒ ìƒìˆ˜." +msgstr "ìƒìˆ˜ë¥¼ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "글꼴 {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ!" +msgstr "ê¸€ê¼´ì„ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "ì•„ì´ì½˜ {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ!" +msgstr "ì•„ì´ì½˜ì„ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "스타ì¼ë°•스 {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "하위 리소스를 ì°¾ì„ ìˆ˜ 없습니다." +msgstr "스타ì¼ë°•스를 ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "현재 ì„ íƒ {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "ê°€ì ¸ì˜¬ ê²ƒì´ ì„ íƒë˜ì§€ 않았습니다." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "테마 ê°€ì ¸ì˜¤ê¸°" +msgstr "테마 í•ëª©ì„ ê°€ì ¸ì˜¤ëŠ” 중" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "í•목 {n}/{n} ê°€ì ¸ì˜¤ëŠ” 중" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "편집기를 ëŒê¹Œìš”?" +msgstr "편집기를 ì—…ë°ì´íЏ 중" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8531,18 +8521,16 @@ msgid "Finalizing" msgstr "ë¶„ì„ ì¤‘" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "í•„í„°: " +msgstr "í•„í„°:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "ë°ì´í„°ì™€ 함께" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "노드를 ì„ íƒí•˜ì„¸ìš”" +msgstr "ë°ì´í„° ìœ í˜• 별 ì„ íƒ:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8558,9 +8546,8 @@ msgid "Deselect all visible color items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ìƒìˆ˜ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." @@ -8571,9 +8558,8 @@ msgid "Deselect all visible constant items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ê¸€ê¼´ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." @@ -8584,19 +8570,16 @@ msgid "Deselect all visible font items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ì•„ì´ì½˜ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ì•„ì´ì½˜ í•목과 ê·¸ í•ëª©ì˜ ë°ì´í„°ë¥¼ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ì•„ì´ì½˜ í•ëª©ì„ ì„ íƒ í•´ì œí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." @@ -8617,19 +8600,16 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "ëª¨ë‘ ì ‘ê¸°" +msgstr "ìœ í˜•ì„ ì ‘ìŠµë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "ëª¨ë‘ íŽ¼ì¹˜ê¸°" +msgstr "ìœ í˜•ì„ íŽ¼ì¹©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "템플릿 íŒŒì¼ ì„ íƒ" +msgstr "ëª¨ë“ í…Œë§ˆ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8662,6 +8642,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ëª¨ë“ í•목 ì‚ì œ" @@ -8692,6 +8678,12 @@ msgid "Remove All StyleBox Items" msgstr "ëª¨ë“ í•목 ì‚ì œ" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "í´ëž˜ìФ í•목 추가" @@ -8771,9 +8763,8 @@ msgid "Add Type:" msgstr "ìœ í˜•:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "í•목 추가" +msgstr "í•목 추가:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8781,9 +8772,8 @@ msgid "Add StyleBox Item" msgstr "ëª¨ë“ í•목 추가" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "í•목 ì‚ì œ" +msgstr "í•목 ì‚ì œ:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" @@ -8824,9 +8814,8 @@ msgid "Editor Theme" msgstr "테마 편집" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "리소스 ì‚ì œ" +msgstr "다른 테마 리소스 ì„ íƒ:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8869,9 +8858,8 @@ msgid "Add Item Type" msgstr "í•목 추가" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "노드 ìœ í˜•" +msgstr "노드 ìœ í˜•:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8892,9 +8880,8 @@ msgid "Override all default type items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "테마" +msgstr "테마:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8959,9 +8946,8 @@ msgid "Checked Radio Item" msgstr "ì²´í¬ëœ ë¼ë””오 í•목" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "ì´ë¦„있는 구분ìž." +msgstr "ì´ë¦„ 있는 구분ìž" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9135,7 +9121,7 @@ msgstr "TileSetì— í…스처를 추가합니다." #: editor/plugins/tile_set_editor_plugin.cpp msgid "Remove selected Texture from TileSet." -msgstr "ì„ íƒí•œ í…스처를 TileSetì—서 ì œê±°í•©ë‹ˆë‹¤." +msgstr "ì„ íƒëœ í…스처를 TileSetì—서 ì‚ì œí•©ë‹ˆë‹¤." #: editor/plugins/tile_set_editor_plugin.cpp msgid "Create from Scene" @@ -10399,9 +10385,8 @@ msgid "VisualShader" msgstr "비주얼 ì…°ì´ë”" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "비주얼 ì†ì„± 편집" +msgstr "비주얼 ì†ì„± 편집:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10844,14 +10829,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "한 ë²ˆì— %dê°œì˜ í”„ë¡œì 트를 ì‹¤í–‰í• ê±´ê°€ìš”?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "목ë¡ì—서 기기 ì„ íƒ" +msgstr "목ë¡ì—서 프로ì 트 %d개를 ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "목ë¡ì—서 기기 ì„ íƒ" +msgstr "목ë¡ì—서 ì´ í”„ë¡œì 트를 ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/project_manager.cpp msgid "" @@ -12312,6 +12295,16 @@ msgstr "ìº¡ìŠ ëª¨ì–‘ ë†’ì´ ë°”ê¾¸ê¸°" msgid "Change Ray Shape Length" msgstr "ê´‘ì„ ëª¨ì–‘ ê¸¸ì´ ë°”ê¾¸ê¸°" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ê³¡ì„ ì 위치 ì„¤ì •" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ê³¡ì„ ì 위치 ì„¤ì •" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ì›ê¸°ë‘¥ 반지름 바꾸기" @@ -13109,17 +13102,15 @@ msgstr "목ë¡ì—서 기기 ì„ íƒ" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "%sì—서 실행" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "ëª¨ë‘ ë‚´ë³´ë‚´ê¸°" +msgstr "APK로 내보내는 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "ì‚ì œ" +msgstr "ì œê±° 중..." #: platform/android/export/export.cpp #, fuzzy @@ -13127,14 +13118,12 @@ msgid "Installing to device, please wait..." msgstr "로드 중, ê¸°ë‹¤ë ¤ 주세요..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "ì”¬ì„ ì¸ìŠ¤í„´ìŠ¤ í• ìˆ˜ 없습니다!" +msgstr "ê¸°ê¸°ì— ì„¤ì¹˜í• ìˆ˜ ì—†ìŒ: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "맞춤 스í¬ë¦½íЏ 실행 중..." +msgstr "기기ì—서 실행 중..." #: platform/android/export/export.cpp #, fuzzy @@ -13163,7 +13152,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Debug keystore not configured in the Editor Settings nor in the preset." -msgstr "Debug keystore를 편집기 ì„¤ì •ê³¼ í”„ë¦¬ì…‹ì— ì„¤ì •í•˜ì§€ 않았습니다." +msgstr "Debug keystore를 편집기 ì„¤ì •ê³¼ í”„ë¦¬ì…‹ì— êµ¬ì„±í•˜ì§€ 않았습니다." #: platform/android/export/export.cpp msgid "" @@ -13175,7 +13164,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Release keystore incorrectly configured in the export preset." -msgstr "내보내기 í”„ë¦¬ì…‹ì— ë°°í¬ keystorkeê°€ 잘못 ì„¤ì •ë˜ì–´ 있습니다." +msgstr "내보내기 í”„ë¦¬ì…‹ì— ì¶œì‹œ keystorkeê°€ 잘못 구성ë˜ì–´ 있습니다." #: platform/android/export/export.cpp msgid "A valid Android SDK path is required in Editor Settings." @@ -13187,7 +13176,7 @@ msgstr "편집기 ì„¤ì •ì—서 ìž˜ëª»ëœ Android SDK 경로입니다." #: platform/android/export/export.cpp msgid "Missing 'platform-tools' directory!" -msgstr "'platform-tools' ë””ë ‰í„°ë¦¬ê°€ 없습니다!" +msgstr "'platform-tools' ë””ë ‰í† ë¦¬ê°€ 없습니다!" #: platform/android/export/export.cpp msgid "Unable to find Android SDK platform-tools' adb command." @@ -13195,11 +13184,11 @@ msgstr "Android SDK platform-toolsì˜ adb ëª…ë ¹ì„ ì°¾ì„ ìˆ˜ 없습니다." #: platform/android/export/export.cpp msgid "Please check in the Android SDK directory specified in Editor Settings." -msgstr "편집기 ì„¤ì •ì—서 ì§€ì •ëœ Android SDK ë””ë ‰í„°ë¦¬ë¥¼ 확ì¸í•´ì£¼ì„¸ìš”." +msgstr "편집기 ì„¤ì •ì—서 ì§€ì •ëœ Android SDK ë””ë ‰í† ë¦¬ë¥¼ 확ì¸í•´ì£¼ì„¸ìš”." #: platform/android/export/export.cpp msgid "Missing 'build-tools' directory!" -msgstr "'build-tools' ë””ë ‰í„°ë¦¬ê°€ 없습니다!" +msgstr "'build-tools' ë””ë ‰í† ë¦¬ê°€ 없습니다!" #: platform/android/export/export.cpp msgid "Unable to find Android SDK build-tools' apksigner command." @@ -13263,16 +13252,12 @@ msgid "Signing debug %s..." msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"íŒŒì¼ ìŠ¤ìº”ì¤‘.\n" -"ê¸°ë‹¤ë ¤ì£¼ì‹ì‹œì˜¤..." +msgstr "출시 %sì— ì„œëª… 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "내보내기 í…œí”Œë¦¿ì„ ì—´ 수 ì—†ìŒ:" +msgstr "keystore를 ì°¾ì„ ìˆ˜ 없어, 내보낼 수 없었습니다." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" @@ -13288,9 +13273,8 @@ msgid "'apksigner' verification of %s failed." msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "ëª¨ë‘ ë‚´ë³´ë‚´ê¸°" +msgstr "Android용으로 내보내는 중" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13334,14 +13318,12 @@ msgid "" msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "프로ì 트 ê²½ë¡œì— project.godot 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다." +msgstr "프로ì 트 파ì¼ì„ gradle 프로ì 트로 내보낼 수 없었습니다\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" +msgstr "확장 패키지 파ì¼ì„ 쓸 수 없었습니다!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13353,7 +13335,7 @@ msgid "" "Alternatively visit docs.godotengine.org for Android build documentation." msgstr "" "Android 프로ì íŠ¸ì˜ ë¹Œë“œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤, ì¶œë ¥ëœ ì˜¤ë¥˜ë¥¼ 확ì¸í•˜ì„¸ìš”.\n" -"ë˜ëŠ” docs.godotengine.orgì—서 Andoid 빌드 문서를 찾아보세요." +"ë˜ëŠ” docs.godotengine.orgì—서 Android 빌드 설명문서를 찾아보세요." #: platform/android/export/export.cpp msgid "Moving output" @@ -13365,7 +13347,7 @@ msgid "" "outputs." msgstr "" "내보내기 파ì¼ì„ ë³µì‚¬í•˜ê³ ì´ë¦„ì„ ë°”ê¿€ 수 없습니다, ì¶œë ¥ì— ëŒ€í•œ gradle 프로ì " -"트 ë””ë ‰í„°ë¦¬ë¥¼ 확ì¸í•˜ì„¸ìš”." +"트 ë””ë ‰í† ë¦¬ë¥¼ 확ì¸í•˜ì„¸ìš”." #: platform/android/export/export.cpp #, fuzzy @@ -13373,16 +13355,16 @@ msgid "Package not found: %s" msgstr "ì• ë‹ˆë©”ì´ì…˜ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ: '%s'" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "ìœ¤ê³½ì„ ë§Œë“œëŠ” 중..." +msgstr "APK를 만드는 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "내보내기 í…œí”Œë¦¿ì„ ì—´ 수 ì—†ìŒ:" +msgstr "" +"내보낼 템플릿 APK를 ì°¾ì„ ìˆ˜ ì—†ìŒ:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13393,14 +13375,12 @@ msgid "" msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "%s 추가하는 중..." +msgstr "파ì¼ì„ 추가하는 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" +msgstr "프로ì 트 파ì¼ì„ 내보낼 수 없었습니다" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13455,29 +13435,24 @@ msgid "Could not write file:" msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" +msgstr "파ì¼ì„ ì½ì„ 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "맞춤 HTML shellì„ ì½ì„ 수 ì—†ìŒ:" +msgstr "HTML shellì„ ì½ì„ 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "í´ë”를 만들 수 없습니다." +msgstr "HTTP 서버 ë””ë ‰í† ë¦¬ë¥¼ 만들 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "씬 ì €ìž¥ 중 오류." +msgstr "HTTP 서버를 시작하는 중 오류:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "ìž˜ëª»ëœ ì‹ë³„ìž:" +msgstr "ìž˜ëª»ëœ bundle ì‹ë³„ìž:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." @@ -14052,6 +14027,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "ì´ ë°”ë””ëŠ” 메시를 ì„¤ì •í• ë•Œê¹Œì§€ 무시ë©ë‹ˆë‹¤." @@ -14158,7 +14169,7 @@ msgid "" msgstr "" "색ìƒ: #%s\n" "좌í´ë¦: ìƒ‰ìƒ ì„¤ì •\n" -"ìš°í´ë¦: 프리셋 ì œê±°" +"ìš°í´ë¦: 프리셋 ì‚ì œ" #: scene/gui/color_picker.cpp msgid "Pick a color from the editor window." @@ -15474,9 +15485,6 @@ msgstr "ìƒìˆ˜ëŠ” ìˆ˜ì •í• ìˆ˜ 없습니다." #~ msgid "I see..." #~ msgstr "ì•Œê² ìŠµë‹ˆë‹¤..." -#~ msgid "Can't open '%s'." -#~ msgstr "'%s' 열수 ì—†ìŒ." - #~ msgid "Ugh" #~ msgstr "오우" diff --git a/editor/translations/lt.po b/editor/translations/lt.po index ab98c9b156..f8bc356023 100644 --- a/editor/translations/lt.po +++ b/editor/translations/lt.po @@ -349,6 +349,7 @@ msgstr "Keiskite animacijos ciklo režimÄ…" msgid "Remove Anim Track" msgstr "Animacija: panaikinti įrašą" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -373,10 +374,26 @@ msgstr "Sukurti" msgid "Anim Insert" msgstr "Animacija: įterpti" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacija" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -962,7 +979,7 @@ msgstr "Sukurti NaujÄ…" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2288,6 +2305,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3079,10 +3107,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3710,6 +3734,15 @@ msgid "Download from:" msgstr "Atsisiuntimo Klaida" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Atidaryti" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8555,6 +8588,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "MÄ—gstamiausi:" @@ -8583,6 +8622,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "MÄ—gstamiausi:" @@ -12121,6 +12166,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13758,6 +13811,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/lv.po b/editor/translations/lv.po index 3c6ab5cb19..180cd1be1c 100644 --- a/editor/translations/lv.po +++ b/editor/translations/lv.po @@ -344,6 +344,7 @@ msgstr "IzmainÄ«t AnimÄcijas AtkÄrtoÅ¡anÄs Režīmu" msgid "Remove Anim Track" msgstr "Noņemt Anim. Celiņu" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Izveidot JAUNU celiņu priekÅ¡ %s un ievietot atslÄ“gievietni?" @@ -368,10 +369,26 @@ msgstr "Izveidot" msgid "Anim Insert" msgstr "Anim ievietot" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Funkcijas:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nevar animÄ“t pats sevi, tikai citus spÄ“lÄ“tÄjus." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Izveidot un Ievietot" @@ -950,7 +967,7 @@ msgstr "Izveidot Jaunu %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2270,6 +2287,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3051,10 +3079,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "NepÄrtraukti Atjaunot" @@ -3665,6 +3689,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "AtvÄ“rt Failu PÄrlÅ«kÄ" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8410,6 +8443,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Noņemt no FavorÄ«tiem" @@ -8438,6 +8477,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Pievienot FavorÄ«tiem" @@ -11935,6 +11980,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13556,6 +13609,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/mi.po b/editor/translations/mi.po index 021c55f38b..3a70aade1a 100644 --- a/editor/translations/mi.po +++ b/editor/translations/mi.po @@ -329,6 +329,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -353,10 +354,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -911,7 +927,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2202,6 +2218,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2981,10 +3008,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3589,6 +3612,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8266,6 +8297,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8290,6 +8327,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11697,6 +11740,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13291,6 +13342,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/mk.po b/editor/translations/mk.po index a437ddf1c7..bf449381bb 100644 --- a/editor/translations/mk.po +++ b/editor/translations/mk.po @@ -336,6 +336,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -360,10 +361,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -918,7 +934,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2210,6 +2226,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2989,10 +3016,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3598,6 +3621,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8281,6 +8312,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8305,6 +8342,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11712,6 +11755,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13306,6 +13357,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ml.po b/editor/translations/ml.po index 8213c2251c..b0d3a5a8d7 100644 --- a/editor/translations/ml.po +++ b/editor/translations/ml.po @@ -339,6 +339,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -363,10 +364,27 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ചലനം à´šàµà´±àµà´±àµ½" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "à´—àµà´£à´‚ നോകàµà´•àµà´•" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -921,7 +939,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2215,6 +2233,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2994,10 +3023,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3602,6 +3627,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8287,6 +8320,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8311,6 +8350,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11719,6 +11764,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13315,6 +13368,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/mr.po b/editor/translations/mr.po index b459ca23f3..af59635c8a 100644 --- a/editor/translations/mr.po +++ b/editor/translations/mr.po @@ -336,6 +336,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -360,10 +361,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "अâ€à¥…निमेशन टà¥à¤°à¥€" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -918,7 +935,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2209,6 +2226,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2989,10 +3017,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3597,6 +3621,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8279,6 +8311,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8303,6 +8341,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11713,6 +11757,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13308,6 +13360,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ms.po b/editor/translations/ms.po index 51a09d2e24..5fd2547bcb 100644 --- a/editor/translations/ms.po +++ b/editor/translations/ms.po @@ -344,6 +344,7 @@ msgstr "Tukar Mod Gelung Animasi" msgid "Remove Anim Track" msgstr "Keluarkan Trek Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Cipta trek BARU untuk %s dan masukkan kunci?" @@ -368,10 +369,27 @@ msgstr "Cipta" msgid "Anim Insert" msgstr "Masukkan Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Set Peralihan ke:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer tidak animasikan dirinya sendiri, hanya pemain lain." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Sifat" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Cipta & Masukkan" @@ -950,7 +968,7 @@ msgstr "Cipta %s Baru" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2302,6 +2320,17 @@ msgid "New Window" msgstr "Tetingkap Baru" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Berputar apabila tingkap editor dilukis semula." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Sumber yang diimport tidak dapat disimpan." @@ -3165,10 +3194,6 @@ msgid "Save & Restart" msgstr "Simpan & Mula Semula" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Berputar apabila tingkap editor dilukis semula." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Kemas Kini Secara Berterusan" @@ -3828,6 +3853,15 @@ msgid "Download from:" msgstr "Muat turun" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Buka dalam Pengurus Fail" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8593,6 +8627,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Keluarkan Item" @@ -8623,6 +8663,12 @@ msgid "Remove All StyleBox Items" msgstr "Keluarkan Item" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Tambah ke Kegemaran" @@ -12087,6 +12133,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13712,6 +13766,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/nb.po b/editor/translations/nb.po index efbca6253d..02f32b055b 100644 --- a/editor/translations/nb.po +++ b/editor/translations/nb.po @@ -353,6 +353,7 @@ msgstr "Endre løkkemodus for animasjon" msgid "Remove Anim Track" msgstr "Fjern Anim-Spor" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Lag NYTT spor for %s og sett inn nøkkel?" @@ -377,10 +378,28 @@ msgstr "Lag" msgid "Anim Insert" msgstr "Anim Sett inn" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan ikke Ã¥pne '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasjon" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimasjonAvspiller kan ikke animere seg selv, kun andre avspillere." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Egenskapen «%s» eksisterer ikke." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Lag og Sett Inn" @@ -971,7 +990,7 @@ msgstr "Lag ny %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2384,6 +2403,17 @@ msgid "New Window" msgstr "Nytt vindu" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Snurrer nÃ¥r redigeringsvinduet tegner opp pÃ¥ nytt." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importerte ressurser kan ikke lagres." @@ -3260,10 +3290,6 @@ msgid "Save & Restart" msgstr "Lagre & Avslutt" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Snurrer nÃ¥r redigeringsvinduet tegner opp pÃ¥ nytt." - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Kontinuerlig" @@ -3931,6 +3957,16 @@ msgid "Download from:" msgstr "Nedlastningsfeil" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Vis I Filutforsker" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopier feil" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8990,6 +9026,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Fjern Funksjon" @@ -9020,6 +9062,12 @@ msgid "Remove All StyleBox Items" msgstr "Fjern Funksjon" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Legg til Element" @@ -12688,6 +12736,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Fjern Funksjon" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Fjern Funksjon" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14382,6 +14440,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -15259,9 +15353,6 @@ msgstr "Konstanter kan ikke endres." #~ msgid "I see..." #~ msgstr "Jeg forstÃ¥r..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan ikke Ã¥pne '%s'." - #~ msgid "Ugh" #~ msgstr "Æsj" diff --git a/editor/translations/nl.po b/editor/translations/nl.po index 2f16b90840..00f87ef79c 100644 --- a/editor/translations/nl.po +++ b/editor/translations/nl.po @@ -48,12 +48,13 @@ # Arthur de Roos <arthur.de.roos@gmail.com>, 2021. # Vancha March <tjipkevdh@gmail.com>, 2021. # Hugo van de Kuilen <hugo.vandekuilen1234567890@gmail.com>, 2021. +# tobeqz <vanveenjorik+tobeqz@gmail.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-08-02 02:00+0000\n" -"Last-Translator: Hugo van de Kuilen <hugo.vandekuilen1234567890@gmail.com>\n" +"PO-Revision-Date: 2021-08-06 19:42+0000\n" +"Last-Translator: tobeqz <vanveenjorik+tobeqz@gmail.com>\n" "Language-Team: Dutch <https://hosted.weblate.org/projects/godot-engine/godot/" "nl/>\n" "Language: nl\n" @@ -384,6 +385,7 @@ msgstr "Animatielusmodus veranderen" msgid "Remove Anim Track" msgstr "Verwijder Anim Track" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "NIEUW spoor aanmaken voor %s en sleutel invoegen?" @@ -408,10 +410,28 @@ msgstr "Maken" msgid "Anim Insert" msgstr "Anim Invoegen" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan '%s' niet openen." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animatie" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Animatie-Speler kan zichzelf niet animeren, alleen andere spelers." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Eigenschap '%s' bestaat niet." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Maken & Invoegen" @@ -622,9 +642,8 @@ msgid "Go to Previous Step" msgstr "Ga naar Vorige Stap" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Resetten" +msgstr "Reset" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -643,9 +662,8 @@ msgid "Use Bezier Curves" msgstr "Gebruik Bezier Curves" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Plak sporen" +msgstr "Creëer RESET Track(s)" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -992,9 +1010,9 @@ msgstr "%s opstellen" msgid "No results for \"%s\"." msgstr "Geen resultaten voor \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Geen beschrijving beschikbaar voor %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1094,7 +1112,6 @@ msgid "Owners Of:" msgstr "Eigenaren van:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " @@ -1102,11 +1119,10 @@ msgid "" msgstr "" "Geselecteerde bestanden uit het project verwijderen? (Kan niet ongedaan " "gemaakt worden)\n" -"De bestanden kunnen mogelijk vanuit de prullenbak van het systeem hersteld " -"worden." +"Bestanden kunnen naar de prullenbak gestuurd worden of permanent verwijderd " +"worden, afhankelijk van uw bestandssysteem." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1117,8 +1133,8 @@ msgstr "" "De bestanden die verwijderd worden zijn nodig om andere bronnen te laten " "werken.\n" "Toch verwijderen? (Onomkeerbaar)\n" -"De bestanden kunnen mogelijk vanuit de prullenbak van het systeem hersteld " -"worden." +"De bestanden kunnen naar de prullenbak gestuurd worden of permanent " +"verwijderd worden, afhankelijk van uw bestandssysteem." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1288,7 +1304,6 @@ msgid "Licenses" msgstr "Licenties" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "Fout bij het openen van het pakketbestand, geen zip-formaat." @@ -1300,29 +1315,28 @@ msgstr "%s (bestaat al)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Inhoud van asset \"%s\" - %d bestand(en) zijn in conflict met uw project:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Inhoud van asset \"%s\" - Geen bestanden hebben een conflict met uw project:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Bronnen aan het uitpakken" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "De volgende bestanden konden niet worden uitgepakt:" +msgstr "De volgende bestanden konden niet worden uitgepakt uit \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "En nog %s bestand(en)." +msgstr "(en nog %s bestanden)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Pakket succesvol geïnstalleerd!" +msgstr "Asset \"%s\" succesvol geïnstalleerd!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1334,9 +1348,8 @@ msgid "Install" msgstr "Installeer" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Pakketinstalleerder" +msgstr "Assetinstalleerder" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1399,7 +1412,6 @@ msgid "Bypass" msgstr "Omleiden" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Audiobusopties" @@ -1567,13 +1579,12 @@ msgid "Can't add autoload:" msgstr "Autoload kan niet toevoegd worden:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Bestand bestaat niet." +msgstr "%s is een ongeldig pad. Bestand bestaat niet." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s is een ongeldig pad. Niet in bron pad (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1773,7 +1784,7 @@ msgstr "Importtabblad" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Laat u 3D scenes weergeven en bewerken." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." @@ -2345,6 +2356,17 @@ msgid "New Window" msgstr "Nieuw Venster" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Draait wanneer het editor venster wordt hertekend." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Geïmporteerde bronnen kunnen niet opgeslagen worden." @@ -3205,10 +3227,6 @@ msgid "Save & Restart" msgstr "Opslaan & Herstarten" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Draait wanneer het editor venster wordt hertekend." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Continu Bijwerken" @@ -3874,6 +3892,16 @@ msgid "Download from:" msgstr "Downloadfout" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Uitvoeren in Browser" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopieer Fout" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8745,6 +8773,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Verwijder Alle Items" @@ -8775,6 +8809,12 @@ msgid "Remove All StyleBox Items" msgstr "Verwijder Alle Items" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Class Items Toevoegen" @@ -12447,6 +12487,16 @@ msgstr "Wijzig Cylinder Vorm Hoogte" msgid "Change Ray Shape Length" msgstr "Wijzig Ray Vorm Lengte" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Zet Curve Punt Positie" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Zet Curve Punt Positie" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Wijzig Cylinder Straal" @@ -14216,6 +14266,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Dit lichaam zal worden genegeerd totdat je een mesh instelt." @@ -15415,9 +15501,6 @@ msgstr "Constanten kunnen niet worden aangepast." #~ msgid "I see..." #~ msgstr "Ik snap het..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan '%s' niet openen." - #~ msgid "Ugh" #~ msgstr "Oeps" diff --git a/editor/translations/or.po b/editor/translations/or.po index 89cbdfbf2b..8bee62f8d5 100644 --- a/editor/translations/or.po +++ b/editor/translations/or.po @@ -335,6 +335,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -359,10 +360,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -917,7 +933,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2208,6 +2224,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2987,10 +3014,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3595,6 +3618,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8272,6 +8303,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8296,6 +8333,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11703,6 +11746,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13297,6 +13348,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/pl.po b/editor/translations/pl.po index edaf8ab701..24ad379ad0 100644 --- a/editor/translations/pl.po +++ b/editor/translations/pl.po @@ -384,6 +384,7 @@ msgstr "ZmieÅ„ sposób zapÄ™tlania animacji" msgid "Remove Anim Track" msgstr "UsuÅ„ Å›cieżkÄ™ animacji" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Utworzyć NOWÄ„ Å›cieżkÄ™ dla %s i wstawić klucz?" @@ -408,11 +409,29 @@ msgstr "Utwórz" msgid "Anim Insert" msgstr "Wstaw animacjÄ™" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Nie można otworzyć '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacja" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer nie może animować sam siebie, tylko inne wÄ™zÅ‚y tego typu." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "WÅ‚aÅ›ciwość \"%s\" nie istnieje." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Utwórz i wstaw" @@ -987,7 +1006,7 @@ msgstr "Utwórz nowy %s" msgid "No results for \"%s\"." msgstr "Brak wyników dla \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2333,6 +2352,17 @@ msgid "New Window" msgstr "Nowe okno" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Obraca siÄ™, gdy okno edytora jest przerysowywane." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Zaimportowane zasoby nie mogÄ… być zapisane." @@ -3192,10 +3222,6 @@ msgid "Save & Restart" msgstr "Zapisz i zrestartuj" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Obraca siÄ™, gdy okno edytora jest przerysowywane." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Aktualizuj ciÄ…gle" @@ -3856,6 +3882,16 @@ msgid "Download from:" msgstr "Błąd pobierania" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Uruchom w przeglÄ…darce" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopiuj błąd" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8727,6 +8763,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "UsuÅ„ wszystkie elementy" @@ -8757,6 +8799,12 @@ msgid "Remove All StyleBox Items" msgstr "UsuÅ„ wszystkie elementy" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Dodaj klasÄ™ elementów" @@ -12419,6 +12467,16 @@ msgstr "ZmieÅ„ wysokość ksztaÅ‚tu cylindra" msgid "Change Ray Shape Length" msgstr "Zmień dÅ‚ugość Ray Shape" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Ustaw pozycje punktu krzywej" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Ustaw pozycje punktu krzywej" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ZmieÅ„ promień cylindra" @@ -14200,6 +14258,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "To ciaÅ‚o bÄ™dzie ignorowane, dopóki nie ustawisz siatki." @@ -15477,9 +15571,6 @@ msgstr "StaÅ‚e nie mogÄ… być modyfikowane." #~ msgid "I see..." #~ msgstr "WidzÄ™..." -#~ msgid "Can't open '%s'." -#~ msgstr "Nie można otworzyć '%s'." - #~ msgid "Ugh" #~ msgstr "Błąd" diff --git a/editor/translations/pr.po b/editor/translations/pr.po index 1bcfe47610..96fab899cd 100644 --- a/editor/translations/pr.po +++ b/editor/translations/pr.po @@ -360,6 +360,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -384,10 +385,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Yer functions:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -962,7 +979,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2291,6 +2308,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3086,10 +3114,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3720,6 +3744,15 @@ msgid "Download from:" msgstr "Discharge ye' Variable" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Slit th' Node" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8562,6 +8595,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Discharge ye' Variable" @@ -8592,6 +8631,12 @@ msgid "Remove All StyleBox Items" msgstr "Discharge ye' Variable" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Add Node" @@ -12150,6 +12195,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Discharge ye' Signal" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Discharge ye' Signal" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13820,6 +13875,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/pt.po b/editor/translations/pt.po index e51c7a3b77..1c8e2476a3 100644 --- a/editor/translations/pt.po +++ b/editor/translations/pt.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-03 10:33+0000\n" -"Last-Translator: André Silva <andre.olivais@gmail.com>\n" +"PO-Revision-Date: 2021-08-06 06:48+0000\n" +"Last-Translator: João Lopes <linux-man@hotmail.com>\n" "Language-Team: Portuguese <https://hosted.weblate.org/projects/godot-engine/" "godot/pt/>\n" "Language: pt\n" @@ -354,6 +354,7 @@ msgstr "Mudar Modo do Loop da Animação" msgid "Remove Anim Track" msgstr "Remover Pista de Animação" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Criar NOVA pista para %s e inserir chave?" @@ -378,12 +379,30 @@ msgstr "Criar" msgid "Anim Insert" msgstr "Anim Inserir" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "ImpossÃvel abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animação" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer não se pode animar a ele próprio, apenas a outros " "executantes." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Não existe a Propriedade '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Criar & Inserir" @@ -591,9 +610,8 @@ msgid "Go to Previous Step" msgstr "Ir para Passo Anterior" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Repor" +msgstr "Aplicar Reinicialização" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -612,9 +630,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Colar Pistas" +msgstr "Criar Pista(s) RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -939,7 +956,6 @@ msgid "Edit..." msgstr "Editar..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Ir para Método" @@ -959,9 +975,9 @@ msgstr "Criar Novo %s" msgid "No results for \"%s\"." msgstr "Nenhum resultado para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Nenhuma descrição disponÃvel para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1061,17 +1077,16 @@ msgid "Owners Of:" msgstr "Proprietários de:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Remover ficheiros selecionados do Projeto? (sem desfazer)\n" -"Pode encontrar os ficheiros removidos na Reciclagem do sistema." +"Remover ficheiros selecionados do Projeto? (Sem desfazer.)\n" +"Dependendo da configuração, pode encontrar os ficheiros removidos na " +"Reciclagem do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1081,8 +1096,9 @@ msgid "" msgstr "" "Os ficheiros a serem removidos são necessários para que outros recursos " "funcionem.\n" -"Remover mesmo assim? (sem desfazer)\n" -"Pode encontrar os ficheiros removidos na Reciclagem do sistema." +"Remover mesmo assim? (Sem desfazer.)\n" +"Dependendo da configuração, pode encontrar os ficheiros removidos na " +"Reciclagem do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1252,14 +1268,13 @@ msgid "Licenses" msgstr "Licenças" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Erro ao abrir ficheiro comprimido (não está no formato ZIP)." +msgstr "" +"Erro ao abrir ficheiro de recurso para \"%s\" (não está no formato ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Já Existe)" +msgstr "%s (já existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" @@ -1274,19 +1289,16 @@ msgid "Uncompressing Assets" msgstr "A Descomprimir Ativos" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Falhou a extração dos seguintes Ficheiros do pacote:" +msgstr "Falhou a extração dos seguintes ficheiros do recurso \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "E mais %s ficheiros." +msgstr "(e mais %s ficheiros)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Pacote Instalado com sucesso!" +msgstr "Recurso \"%s\" instalado com sucesso!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1298,9 +1310,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de Pacotes" +msgstr "Instalador de Recursos" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -2310,6 +2321,17 @@ msgid "New Window" msgstr "Nova Janela" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Roda quando a janela do editor atualiza." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Recursos importados não podem ser guardados." @@ -3171,10 +3193,6 @@ msgid "Save & Restart" msgstr "Guardar & Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Roda quando a janela do editor atualiza." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Atualização ContÃnua" @@ -3839,6 +3857,16 @@ msgid "Download from:" msgstr "Erro na transferência" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Executar no Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Erro" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8698,6 +8726,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Remover Todos os Itens" @@ -8728,6 +8762,12 @@ msgid "Remove All StyleBox Items" msgstr "Remover Todos os Itens" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Adicionar Itens de Classe" @@ -12385,6 +12425,16 @@ msgstr "Mudar Altura da Forma Cilindro" msgid "Change Ray Shape Length" msgstr "Mudar comprimento da forma raio" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Definir posição do Ponto da curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Definir posição do Ponto da curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Mudar Raio do Cilindro" @@ -14158,6 +14208,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este corpo será ignorado até se definir uma malha." @@ -15596,9 +15682,6 @@ msgstr "Constantes não podem ser modificadas." #~ msgid "I see..." #~ msgstr "Eu vejo..." -#~ msgid "Can't open '%s'." -#~ msgstr "ImpossÃvel abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po index c11e4f347b..b7bb7ce0c4 100644 --- a/editor/translations/pt_BR.po +++ b/editor/translations/pt_BR.po @@ -95,14 +95,14 @@ # Felipe Jesus Macedo <fmacedo746@gmail.com>, 2020. # José Paulo <jose.paulo1919@gmail.com>, 2020. # Necco <necco@outlook.com>, 2020. -# Marcelo Silveira Hayden <mshayden.1998@gmail.com>, 2020. +# Marcelo Silveira Hayden <mshayden.1998@gmail.com>, 2020, 2021. # GUILHERME SOUZA REIS DE MELO LOPES <guilhermesrml@unipam.edu.br>, 2020. # Gabriela Araújo <Gabirin@outlook.com.br>, 2020. # Jairo Tuboi <tuboi.jairo@gmail.com>, 2020. # Felipe Fetter <felipetfetter@gmail.com>, 2020. # Rafael Henrique Capati <rhcapati@gmail.com>, 2020. # NogardRyuu <nogardryuu@gmail.com>, 2020, 2021. -# Elton <eltondeoliveira@outlook.com>, 2020. +# Elton <eltondeoliveira@outlook.com>, 2020, 2021. # ThiagoCTN <thiagocampostn@gmail.com>, 2020. # Alec Santos <alecsantos96@gmail.com>, 2020. # Augusto Milão <augusto.milao01@gmail.com>, 2021. @@ -118,12 +118,14 @@ # Gustavo HM 102 <gustavohm102@gmail.com>, 2021. # Douglas Leão <djlsplays@gmail.com>, 2021. # PauloFRs <paulofr1@hotmail.com>, 2021. +# Diego Bloise <diego-dev@outlook.com>, 2021. +# Alkoarism <Alkoarism@gmail.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: 2016-05-30\n" -"PO-Revision-Date: 2021-06-29 08:04+0000\n" -"Last-Translator: PauloFRs <paulofr1@hotmail.com>\n" +"PO-Revision-Date: 2021-08-06 06:47+0000\n" +"Last-Translator: Alkoarism <Alkoarism@gmail.com>\n" "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/" "godot-engine/godot/pt_BR/>\n" "Language: pt_BR\n" @@ -131,7 +133,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7.1-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -452,6 +454,7 @@ msgstr "Alterar Modo Repetição da Animação" msgid "Remove Anim Track" msgstr "Remover Trilha da Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Criar NOVA faixa para %s e inserir chave?" @@ -476,10 +479,28 @@ msgstr "Criar" msgid "Anim Insert" msgstr "Inserir Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Não é possÃvel abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animação" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer não pode animar a si mesmo, apenas outros jogadores." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Nenhuma propriedade '%s' existe." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Criar e Inserir Anim" @@ -689,9 +710,8 @@ msgid "Go to Previous Step" msgstr "Ir ao Passo Anterior" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Recompor" +msgstr "Redefinir" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -710,9 +730,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas de Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Colar Trilhas" +msgstr "Criar RESET Track(s)" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -785,7 +804,7 @@ msgstr "Mudar Deslocamento do InÃcio do Clip de Trilha de Audio" #: editor/animation_track_editor_plugins.cpp msgid "Change Audio Track Clip End Offset" -msgstr "Alterar Deslocamento de Fim do Clipe de Faixa de Ãudio" +msgstr "Alterar fim da Trilha de Aúdio" #: editor/array_property_edit.cpp msgid "Resize Array" @@ -1056,9 +1075,9 @@ msgstr "Criar Novo %s" msgid "No results for \"%s\"." msgstr "Sem resultados para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Sem descrição disponÃvel para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1158,18 +1177,16 @@ msgid "Owners Of:" msgstr "Donos De:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Remover arquivos selecionados do projeto? (irreversÃvel)\n" -"Você pode encontrar os arquivos removidos na lixeira do sistema para " -"restaurá-los." +"Remover arquivos selecionados do projeto? (IrreversÃvel.)\n" +"Dependendo da configuração do seu sistema, os arquivos serão movidos para a " +"lixeira do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1177,11 +1194,11 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Os arquivos sendo removidos são requeridos por outros recursos para que " -"funcionem.\n" -"Removê-los mesmo assim? (irreversÃvel)\n" -"Você pode encontrar os arquivos removidos na lixeira do sistema para " -"restaurá-los." +"Os arquivos que estão sendo removidos são necessários por outros recursos " +"para que funcionem.\n" +"Removê-los mesmo assim? (IrreversÃvel.)\n" +"Dependendo da configuração do seu sistema, os arquivos serão movidos para a " +"lixeira do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1351,9 +1368,8 @@ msgid "Licenses" msgstr "Licenças" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Erro ao abrir arquivo compactado (não está em formato ZIP)." +msgstr "Erro ao abrir o pacote \"%s\" (não está em formato ZIP)." #: editor/editor_asset_installer.cpp #, fuzzy @@ -1362,30 +1378,30 @@ msgstr "%s (Já existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "Conteúdo do asset \"%s\" - %d arquivo(s) conflita(m) com seu projeto:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Conteúdo do asset \"%s\" - Nenhum arquivo entra em conflito com o seu " +"projeto:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Descompactando Assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Os arquivos a seguir falharam ao serem extraÃdos do pacote:" +msgstr "Os seguintes arquivos falharam na extração do asset \"% s\":" #: editor/editor_asset_installer.cpp #, fuzzy msgid "(and %s more files)" -msgstr "%s mais arquivo(s)." +msgstr "(e %s mais arquivos)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Pacote instalado com sucesso!" +msgstr "Asset \"%s\" instalados com sucesso!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1397,9 +1413,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de Pacotes" +msgstr "Instalador de Assets" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1632,11 +1647,11 @@ msgstr "Não pode adicionar autoload:" #: editor/editor_autoload_settings.cpp #, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "O arquivo não existe." +msgstr "O %s é um caminho inválido. O arquivo não existe." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s é um caminho inválido. Não está no caminho dos recursos (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1662,7 +1677,7 @@ msgstr "Nome" #: editor/editor_autoload_settings.cpp #, fuzzy msgid "Global Variable" -msgstr "Variável" +msgstr "Variável Global" #: editor/editor_data.cpp msgid "Paste Params" @@ -1763,8 +1778,8 @@ msgid "" "Target platform requires 'PVRTC' texture compression for GLES2. Enable " "'Import Pvrtc' in Project Settings." msgstr "" -"A plataforma alvo requer compressão de texturas 'PVRTC' para GLES2. Habilite " -"'Importar Pvrtc' nas Configurações de Projeto." +"A plataforma de destino requer compressão de texturas 'PVRTC' para GLES2. " +"Habilite 'Importar Pvrtc' nas Configurações de Projeto." #: editor/editor_export.cpp msgid "" @@ -1836,36 +1851,43 @@ msgid "Import Dock" msgstr "Importar Dock" #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite visualizar e editar cenas 3D." #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Permite editar scripts usando o editor de script integrado." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Fornece acesso integrado à Biblioteca de Assets." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permite editar a hierarquia de nó na doca Cena." #: editor/editor_feature_profile.cpp +#, fuzzy msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." -msgstr "" +msgstr "Permite trabalhar com sinais e grupos do nó selecionado na doca Cena." #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permite navegar pelo sistema de arquivos local através de uma doca dedicada." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Permite definir as configurações de importação para assets individualmente. " +"Requer a doca FileSystem para funcionar." #: editor/editor_feature_profile.cpp #, fuzzy @@ -1874,11 +1896,11 @@ msgstr "(Atual)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(Nenhum(a))" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "Remover o perfil selecionado, '%s'? Não pode ser desfeita." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1911,17 +1933,16 @@ msgstr "Habilitar Editor Contextual" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Class Properties:" -msgstr "Propriedades:" +msgstr "Propriedades de Classe:" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Main Features:" -msgstr "Funcionalidades" +msgstr "CaracterÃsticas principais:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Classes Ativadas:" +msgstr "Nós e Classes:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1939,9 +1960,8 @@ msgid "Error saving profile to path: '%s'." msgstr "Erro ao salvar perfil no caminho: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Redefinir para os padrões" +msgstr "Redefinir padrões" #: editor/editor_feature_profile.cpp msgid "Current Profile:" @@ -1950,12 +1970,12 @@ msgstr "Perfil Atual:" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Create Profile" -msgstr "Apagar Perfil" +msgstr "Criar Perfil" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Remove Profile" -msgstr "Remover Telha" +msgstr "Remover Perfil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1977,16 +1997,19 @@ msgstr "Exportação" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Configure Selected Profile:" -msgstr "Perfil Atual:" +msgstr "Configurar Perfil Selecionado:" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Extra Options:" -msgstr "Opções da Textura" +msgstr "Opções Extra:" #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Criar ou importar um perfil para editar as classes e propriedades " +"disponÃveis." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -2015,7 +2038,7 @@ msgstr "Selecionar a Pasta Atual" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp #, fuzzy msgid "File exists, overwrite?" -msgstr "O arquivo existe. Sobrescrever?" +msgstr "O arquivo já existe. Sobrescrever?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2408,6 +2431,17 @@ msgid "New Window" msgstr "Nova Janela" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira quando a janela do editor atualiza." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Recursos Importados não podem ser salvos." @@ -2638,13 +2672,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"A cena atual não tem um nó raiz, mas %d recurso(s) externo(s) modificado(s) " +"foram salvos de qualquer forma." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Um nó raiz é requerido para salvar a cena." +msgstr "" +"Um nó-raiz é necessário para salvar a cena. Você pode adicionar um nó-raiz " +"usando a doca da árvore de cenas." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2752,9 +2789,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Unable to find script field for addon plugin at: '%s'." -msgstr "" -"Não foi possÃvel localizar a área do script para o complemento do plugin em: " -"'%s'." +msgstr "Não foi possÃvel encontrar o campo de script para o plugin em: '%s'." #: editor/editor_node.cpp msgid "Unable to load addon script from path: '%s'." @@ -3035,7 +3070,7 @@ msgstr "Explorador de Recursos Órfãos..." #: editor/editor_node.cpp #, fuzzy msgid "Reload Current Project" -msgstr "Renomear Projeto" +msgstr "Recarregar o projeto atual" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3197,11 +3232,12 @@ msgstr "Ajuda" #: editor/editor_node.cpp #, fuzzy msgid "Online Documentation" -msgstr "Abrir Documentação" +msgstr "Documentação Online" #: editor/editor_node.cpp +#, fuzzy msgid "Questions & Answers" -msgstr "" +msgstr "Perguntas & Respostas" #: editor/editor_node.cpp msgid "Report a Bug" @@ -3210,7 +3246,7 @@ msgstr "Reportar bug" #: editor/editor_node.cpp #, fuzzy msgid "Suggest a Feature" -msgstr "Defina um Valor" +msgstr "Sugira um recurso" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3223,7 +3259,7 @@ msgstr "Comunidade" #: editor/editor_node.cpp #, fuzzy msgid "About Godot" -msgstr "Sobre" +msgstr "Sobre Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3275,10 +3311,6 @@ msgid "Save & Restart" msgstr "Salvar e Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira quando a janela do editor atualiza." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Atualizar Continuamente" @@ -3323,12 +3355,12 @@ msgstr "Gerenciar Templates" #: editor/editor_node.cpp #, fuzzy msgid "Install from file" -msgstr "Instalar a Partir do Arquivo" +msgstr "Instalar do arquivo" #: editor/editor_node.cpp #, fuzzy msgid "Select android sources file" -msgstr "Selecione uma Malha de origem:" +msgstr "Selecione o arquivo de fontes do Android" #: editor/editor_node.cpp msgid "" @@ -3412,7 +3444,7 @@ msgstr "Selecionar" #: editor/editor_node.cpp #, fuzzy msgid "Select Current" -msgstr "Selecionar a Pasta Atual" +msgstr "Selecione Atual" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3449,7 +3481,7 @@ msgstr "Nenhum sub-recurso encontrado." #: editor/editor_path.cpp #, fuzzy msgid "Open a list of sub-resources." -msgstr "Nenhum sub-recurso encontrado." +msgstr "Abra uma lista de sub-recursos." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3478,12 +3510,12 @@ msgstr "Atualizar" #: editor/editor_plugin_settings.cpp #, fuzzy msgid "Version" -msgstr "Versão:" +msgstr "Versão" #: editor/editor_plugin_settings.cpp #, fuzzy msgid "Author" -msgstr "Autores" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3496,14 +3528,12 @@ msgid "Measure:" msgstr "Medida:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Tempo do Frame (seg)" +msgstr "Tempo do Frame (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Tempo Médio (seg)" +msgstr "Tempo Médio (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3945,6 +3975,16 @@ msgid "Download from:" msgstr "Erro ao baixar" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Rodar no Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Erro" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -4294,15 +4334,15 @@ msgstr "Procurando..." #: editor/find_in_files.cpp msgid "%d match in %d file." -msgstr "%d correspondência em %d arquivo." +msgstr "%d correspondências no arquivo %d." #: editor/find_in_files.cpp msgid "%d matches in %d file." -msgstr "%d correspondências em %d arquivo." +msgstr "%d correspondências no arquivo %d." #: editor/find_in_files.cpp msgid "%d matches in %d files." -msgstr "%d correspondências em %d arquivos." +msgstr "%d correspondências no arquivo %d." #: editor/groups_editor.cpp msgid "Add to Group" @@ -4444,11 +4484,11 @@ msgstr "Selecione o arquivo para importar" #: editor/import_defaults_editor.cpp msgid "Importer:" -msgstr "Importar:" +msgstr "Importador:" #: editor/import_defaults_editor.cpp msgid "Reset to Defaults" -msgstr "Redefinir para os padrões" +msgstr "Redefinir padrões" #: editor/import_dock.cpp msgid "Keep File (No Import)" @@ -4766,7 +4806,7 @@ msgstr "Abrir Editor" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_state_machine_editor.cpp msgid "Open Animation Node" -msgstr "Abrir Animação de Nós" +msgstr "Abrir Nó de Animação" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Triangle already exists." @@ -4833,7 +4873,7 @@ msgstr "Editar Filtros" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Output node can't be added to the blend tree." -msgstr "Nós de saÃda não pode ser adicionado à árvore de mistura." +msgstr "Nó de SaÃda não pode ser adicionado à árvore de mistura." #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Add Node to BlendTree" @@ -8818,6 +8858,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Remover Todos os Itens" @@ -8848,6 +8894,12 @@ msgid "Remove All StyleBox Items" msgstr "Remover Todos os Itens" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Adicionar Itens de Classe" @@ -12508,6 +12560,16 @@ msgstr "Alterar a Altura da Forma do Cilindro" msgid "Change Ray Shape Length" msgstr "Alterar o Comprimento da Forma do Raio" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Definir Posição do Ponto da Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Definir Posição do Ponto da Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Alterar Raio do Cilindro" @@ -14289,6 +14351,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este corpo será ignorado até você definir uma malha." @@ -15546,9 +15644,6 @@ msgstr "Constantes não podem serem modificadas." #~ msgid "I see..." #~ msgstr "Entendo..." -#~ msgid "Can't open '%s'." -#~ msgstr "Não é possÃvel abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/ro.po b/editor/translations/ro.po index f1d65384fc..2b1626bfe2 100644 --- a/editor/translations/ro.po +++ b/editor/translations/ro.po @@ -351,6 +351,7 @@ msgstr "SchimbaÈ›i Bucla AnimaÈ›iei" msgid "Remove Anim Track" msgstr "Elimină Pista Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "CreaÈ›i pistă NOUA pentru %s È™i inseraÈ›i cheie?" @@ -375,10 +376,28 @@ msgstr "CreaÈ›i" msgid "Anim Insert" msgstr "Anim InseraÈ›i" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Mod Snap (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "AnimaÈ›ie" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nu se poate anima singur, doar alÈ›i jucători." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Proprietate" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim CreaÈ›i È™i InseraÈ›i" @@ -955,7 +974,7 @@ msgstr "CreaÈ›i %s Nou" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2315,6 +2334,17 @@ msgid "New Window" msgstr "Fereastră Nouă" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Se roteÈ™te când fereastra editorului se redeschide." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Resursele importate nu pot fi salvate." @@ -3179,10 +3209,6 @@ msgid "Save & Restart" msgstr "Salvează È™i Restartează" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Se roteÈ™te când fereastra editorului se redeschide." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizare continuă" @@ -3821,6 +3847,15 @@ msgid "Download from:" msgstr "Eroare Descărcare" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Execută în Browser" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8780,6 +8815,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "EliminaÈ›i Autoload" @@ -8810,6 +8851,12 @@ msgid "Remove All StyleBox Items" msgstr "EliminaÈ›i Autoload" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Adaugă Obiect" @@ -12407,6 +12454,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Setare poziÈ›ie punct de curbă" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Setare poziÈ›ie punct de curbă" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14064,6 +14121,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14580,9 +14673,6 @@ msgstr "" #~ msgid "Scale Mode (R)" #~ msgstr "Mod Redimensionare (R)" -#~ msgid "Snap Mode (%s)" -#~ msgstr "Mod Snap (%s)" - #~ msgid "Tool Scale" #~ msgstr "Unealtă Dimensiune" diff --git a/editor/translations/ru.po b/editor/translations/ru.po index a5d8b4ea1c..50d4484e4b 100644 --- a/editor/translations/ru.po +++ b/editor/translations/ru.po @@ -102,7 +102,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-10 21:40+0000\n" "Last-Translator: Danil Alexeev <danil@alexeev.xyz>\n" "Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/" "godot/ru/>\n" @@ -112,7 +112,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -435,6 +435,7 @@ msgstr "Изменить режим цикла анимации" msgid "Remove Anim Track" msgstr "Удалить дорожку" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Создать новую дорожку Ð´Ð»Ñ %s и вÑтавить ключ?" @@ -459,10 +460,28 @@ msgstr "Создать" msgid "Anim Insert" msgstr "Ð’Ñтавить" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ÐнимациÑ" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer не может анимировать Ñам ÑебÑ, только других." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "СвойÑтво «%s» не ÑущеÑтвует." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Создать и вÑтавить" @@ -672,9 +691,8 @@ msgid "Go to Previous Step" msgstr "Перейти к предыдущему шагу" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "СброÑить" +msgstr "Применить ÑброÑ" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -693,9 +711,8 @@ msgid "Use Bezier Curves" msgstr "ИÑпользовать кривые Безье" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Ð’Ñтавить треки" +msgstr "Создать дорожку(и) СБРОСÐ" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -1020,7 +1037,6 @@ msgid "Edit..." msgstr "Редактировать..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Перейти к методу" @@ -1040,9 +1056,9 @@ msgstr "Создать %s" msgid "No results for \"%s\"." msgstr "Ðет результатов Ð´Ð»Ñ Â«%s»." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Ðет опиÑÐ°Ð½Ð¸Ñ Ð´Ð»Ñ Â«%s»." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1142,17 +1158,16 @@ msgid "Owners Of:" msgstr "Владельцы:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Удалить выбранные файлы из проекта? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ)\n" -"Ð’Ñ‹ можете найти удалённые файлы в корзине, чтобы воÑÑтановить их." +"Удалить выбранные файлы из проекта? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ.)\n" +"Ð’ завиÑимоÑти от конфигурации вашей файловой ÑиÑтемы файлы будут перемещены " +"в ÑиÑтемную корзину или удалены навÑегда." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1161,8 +1176,9 @@ msgid "" "to the system trash or deleted permanently." msgstr "" "УдалÑемые файлы требуютÑÑ Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾Ð¹ работы других реÑурÑов.\n" -"Ð’ÑÑ‘ равно удалить их? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ)\n" -"Ð’Ñ‹ можете найти удалённые файлы в корзине, чтобы воÑÑтановить их." +"Ð’ÑÑ‘ равно удалить их? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ.)\n" +"Ð’ завиÑимоÑти от конфигурации вашей файловой ÑиÑтемы файлы будут перемещены " +"в ÑиÑтемную корзину или удалены навÑегда." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1332,41 +1348,36 @@ msgid "Licenses" msgstr "Лицензии" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Ошибка при открытии файла пакета (Ðе ÑвлÑетÑÑ ZIP форматом)." +msgstr "Ошибка при открытии файла реÑурÑа «%s» (не в формате ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Уже ÑущеÑтвует)" +msgstr "%s (уже ÑущеÑтвует)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "Содержимое реÑурÑа «%s» - %d файл(ов) конфликтуют Ñ Ð²Ð°ÑˆÐ¸Ð¼ проектом:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "Содержимое реÑурÑа «%s» - Ðет файлов, конфликтующих Ñ Ð²Ð°ÑˆÐ¸Ð¼ проектом:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "РаÑпаковка аÑÑетов" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Следующие файлы не удалоÑÑŒ извлечь из пакета:" +msgstr "Следующие файлы не удалоÑÑŒ извлечь из пакета «%s»:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Ещё %d файла(ов)." +msgstr "(ещё %d файла(ов))" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Пакет уÑпешно уÑтановлен!" +msgstr "РеÑÑƒÑ€Ñ Â«%s» уÑпешно уÑтановлен!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1378,9 +1389,8 @@ msgid "Install" msgstr "УÑтановить" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "УÑтановщик пакетов" +msgstr "УÑтановщик реÑурÑов" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1443,7 +1453,6 @@ msgid "Bypass" msgstr "Обход" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Параметры шины" @@ -1611,13 +1620,12 @@ msgid "Can't add autoload:" msgstr "Ðе удаётÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ автозагрузку:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Файл не ÑущеÑтвует." +msgstr "Ðеверный путь «%s». Файл не ÑущеÑтвует." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s — недопуÑтимый путь. Ðужен реÑурÑный путь (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1641,9 +1649,8 @@ msgid "Name" msgstr "Ðазвание" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "ПеременнаÑ" +msgstr "Ð“Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ" #: editor/editor_data.cpp msgid "Paste Params" @@ -1817,48 +1824,52 @@ msgstr "Панель «Импорт»" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "ПозволÑет проÑматривать и редактировать 3D-Ñцены." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"ПозволÑет редактировать Ñкрипты Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ вÑтроенного редактора Ñкриптов." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "ПредоÑтавлÑет вÑтроенный доÑтуп к Библиотеке реÑурÑов." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "ПозволÑет редактировать иерархию узлов в панели «Сцена»." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"ПозволÑет работать Ñ Ñигналами и группами узла, выбранного в панели «Сцена»." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"ПозволÑет проÑматривать локальную файловую ÑиÑтему через Ñпециальную панель." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"ПозволÑет наÑтраивать параметры импорта Ð´Ð»Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ñ… реÑурÑов. Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ " +"требуетÑÑ Ð¿Ð°Ð½ÐµÐ»ÑŒ Â«Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема»." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Текущий)" +msgstr "(текущий)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(нет)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "Удалить текущий выбранный профиль, «%s»? Ðе может быть отменено." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1890,19 +1901,16 @@ msgid "Enable Contextual Editor" msgstr "Включить контекÑтный редактор" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "СвойÑтва:" +msgstr "СвойÑтва клаÑÑа:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "ВозможноÑти" +msgstr "ОÑновные возможноÑти:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "ДоÑтупные клаÑÑÑ‹:" +msgstr "Узлы и клаÑÑÑ‹:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1921,7 +1929,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ð² «%s»." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "СброÑить наÑтройки" @@ -1930,14 +1937,12 @@ msgid "Current Profile:" msgstr "Текущий профиль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Стереть профиль" +msgstr "Создать профиль" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Удалить тайл" +msgstr "Удалить профиль" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1957,18 +1962,18 @@ msgid "Export" msgstr "ÐкÑпорт" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Текущий профиль:" +msgstr "ÐаÑтроить выбранный профиль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Параметры текÑтуры" +msgstr "Дополнительные параметры:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Создайте или импортируйте профиль Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ñтупных клаÑÑов и " +"ÑвойÑтв." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1995,7 +2000,6 @@ msgid "Select Current Folder" msgstr "Выбрать текущую папку" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" msgstr "Файл ÑущеÑтвует, перезапиÑать?" @@ -2390,6 +2394,17 @@ msgid "New Window" msgstr "Ðовое окно" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ВращаетÑÑ, когда окно редактора перериÑовываетÑÑ." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Импортированные реÑурÑÑ‹ не могут быть Ñохранены." @@ -2621,13 +2636,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ñцена не имеет корневого узла, но %d изменённых внешних реÑурÑов вÑÑ‘ " +"равно были Ñохранены." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñцены требуетÑÑ ÐºÐ¾Ñ€Ð½ÐµÐ²Ð¾Ð¹ узел." +msgstr "" +"Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñцены требуетÑÑ ÐºÐ¾Ñ€Ð½ÐµÐ²Ð¾Ð¹ узел. Ð’Ñ‹ можете добавить его, " +"иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¿Ð°Ð½ÐµÐ»ÑŒ «Сцена»." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3010,9 +3028,8 @@ msgid "Orphan Resource Explorer..." msgstr "Обзор реÑурÑов-Ñирот..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Переименовать проект" +msgstr "Перезагрузить текущий проект" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3171,22 +3188,20 @@ msgid "Help" msgstr "Справка" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Открыть документацию" +msgstr "Онлайн-документациÑ" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "ВопроÑÑ‹ и ответы" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Сообщить об ошибке" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "УÑтановить значение" +msgstr "Предложить функцию" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3197,9 +3212,8 @@ msgid "Community" msgstr "СообщеÑтво" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "О Godot Engine" +msgstr "О Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3251,10 +3265,6 @@ msgid "Save & Restart" msgstr "Сохранить и перезапуÑтить" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ВращаетÑÑ, когда окно редактора перериÑовываетÑÑ." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Ðепрерывное обновление" @@ -3297,14 +3307,12 @@ msgid "Manage Templates" msgstr "Управление шаблонами" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" msgstr "УÑтановить из файла" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Выберите иÑточник полиÑетки:" +msgstr "Выберите файл иÑходников android" #: editor/editor_node.cpp msgid "" @@ -3387,9 +3395,8 @@ msgid "Select" msgstr "Выделение" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Выбрать текущую папку" +msgstr "Выбрать текущий" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3424,9 +3431,8 @@ msgid "No sub-resources found." msgstr "Вложенные реÑурÑÑ‹ не найдены." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Вложенные реÑурÑÑ‹ не найдены." +msgstr "Открыть ÑпиÑок вложенных реÑурÑов." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3453,14 +3459,12 @@ msgid "Update" msgstr "Обновление" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "ВерÑиÑ:" +msgstr "ВерÑиÑ" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Ðвторы" +msgstr "Ðвтор" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3473,14 +3477,12 @@ msgid "Measure:" msgstr "Единица измерениÑ:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Ð’Ñ€ÐµÐ¼Ñ ÐºÐ°Ð´Ñ€Ð° (Ñек.)" +msgstr "Ð’Ñ€ÐµÐ¼Ñ ÐºÐ°Ð´Ñ€Ð° (мÑ)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Среднее Ð²Ñ€ÐµÐ¼Ñ (Ñек.)" +msgstr "Среднее Ð²Ñ€ÐµÐ¼Ñ (мÑ)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3507,6 +3509,12 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Включительно: Включает Ð²Ñ€ÐµÐ¼Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… функций, вызываемых Ñтой функцией.\n" +"ИÑпользуйте Ð´Ð»Ñ Ð²Ñ‹ÑÐ²Ð»ÐµÐ½Ð¸Ñ ÑƒÐ·ÐºÐ¸Ñ… меÑÑ‚.\n" +"\n" +"Субъект: учитывает только времÑ, затраченное в Ñамой функции, а не в других " +"функциÑÑ…, вызываемых Ñтой функцией.\n" +"ИÑпользуйте Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка отдельных функций Ð´Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ð¸." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3628,7 +3636,6 @@ msgid "Paste" msgstr "Ð’Ñтавить" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Преобразовать в %s" @@ -3679,10 +3686,9 @@ msgid "Did you forget the '_run' method?" msgstr "Быть может вы забыли метод _run()?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Зажмите Ctrl, чтобы округлить до целых. Зажмите Shift Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ точных " +"Зажмите %s, чтобы округлить до целых. Зажмите Shift Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ точных " "изменений." #: editor/editor_sub_scene.cpp @@ -3703,49 +3709,43 @@ msgstr "Импортировать из узла:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Открыть папку, Ñодержащую Ñти шаблоны." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Удалить Ñти шаблоны." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "Файла «%s» не ÑущеÑтвует." +msgstr "Ðет доÑтупных зеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Получение зеркал, пожалуйÑта, ждите..." +msgstr "Получение ÑпиÑка зеркал..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Ðачало загрузки..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Ошибка при запроÑе URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." msgstr "Подключение к зеркалу..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Ðевозможно определить Ð¸Ð¼Ñ Ñ…Ð¾Ñта:" +msgstr "Ðе удалоÑÑŒ разрешить запрошенный адреÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Ðе удаётÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒÑÑ Ðº хоÑту:" +msgstr "Ðе удалоÑÑŒ подключитьÑÑ Ðº зеркалу." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Ðет ответа от хоÑта:" +msgstr "Ðет ответа от зеркала." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3753,18 +3753,16 @@ msgid "Request failed." msgstr "Ðе удалоÑÑŒ выполнить запроÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ прошёл, Ñлишком много перенаправлений" +msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ð»ÑÑ Ð¸Ð·-за цикличеÑких перенаправлений." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Ðе удалоÑÑŒ выполнить запроÑ." +msgstr "Ðе удалоÑÑŒ выполнить запроÑ:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Загрузка завершена; извлечение шаблонов..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3783,14 +3781,14 @@ msgid "Error getting the list of mirrors." msgstr "Ошибка при получении ÑпиÑка зеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" -"Ошибка парÑинга JSON ÑпиÑка зеркал. ПожалуйÑта, Ñообщите об Ñтой проблеме!" +"Ошибка при разборе JSON Ñо ÑпиÑком зеркал. ПожалуйÑта, Ñообщите об Ñтой " +"проблеме!" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Лучшее доÑтупное зеркало" #: editor/export_template_manager.cpp msgid "" @@ -3843,24 +3841,20 @@ msgid "SSL Handshake Error" msgstr "Ошибка Ñ€ÑƒÐºÐ¾Ð¿Ð¾Ð¶Ð°Ñ‚Ð¸Ñ SSH" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ архив шаблонов ÑкÑпорта." +msgstr "Ðе удалоÑÑŒ открыть архив шаблонов ÑкÑпорта." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Ðеверный формат version.txt файла внутри шаблонов: %s." +msgstr "Ðеверный формат version.txt внутри файла шаблонов ÑкÑпорта: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Файл version.txt не найден в шаблонах." +msgstr "Файл version.txt не найден внутри файла шаблонов ÑкÑпорта." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ Ð´Ð»Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð²:" +msgstr "Ошибка при Ñоздании пути Ð´Ð»Ñ Ð¸Ð·Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð²:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3871,9 +3865,8 @@ msgid "Importing:" msgstr "ИмпортируетÑÑ:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Удалить верÑию шаблона «%s»?" +msgstr "Удалить шаблоны Ð´Ð»Ñ Ð²ÐµÑ€Ñии «%s»?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3889,58 +3882,65 @@ msgstr "Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ€ÑиÑ:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." -msgstr "" +msgstr "Шаблоны ÑкÑпорта отÑутÑтвуют. Загрузите их или уÑтановите из файла." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Шаблоны ÑкÑпорта уÑтановлены и готовы к иÑпользованию." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Открыть файл" +msgstr "Открыть папку" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." -msgstr "" +msgstr "Открывает папку, Ñодержащую уÑтановленные шаблоны Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑии." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Удалить" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Ðачальное значение Ð´Ð»Ñ Ñчетчика" +msgstr "Удалить шаблоны Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑии." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Ошибка загрузки" +msgstr "Загрузить из:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ЗапуÑтить в браузере" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Копировать ошибку" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Загрузить и уÑтановить" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Загружает и уÑтанавливает шаблоны Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑии Ñ Ð»ÑƒÑ‡ÑˆÐµÐ³Ð¾ из доÑтупных " +"зеркал." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "Официальные шаблоны ÑкÑпорта недоÑтупны Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð¸Ñ… Ñборок." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" msgstr "УÑтановить из файла" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Импортировать шаблоны из ZIP файла" +msgstr "УÑтановить шаблоны из локального файла." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3948,19 +3948,16 @@ msgid "Cancel" msgstr "Отмена" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ архив шаблонов ÑкÑпорта." +msgstr "Отменить загрузку шаблонов." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "УÑтановленные верÑии:" +msgstr "Другие уÑтановленные верÑии:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Удалить" +msgstr "Удалить шаблон" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3975,6 +3972,9 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Шаблоны будут продолжать загружатьÑÑ.\n" +"ПоÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ может произойти кратковременное завиÑание " +"редактора." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4122,35 +4122,32 @@ msgid "Collapse All" msgstr "Свернуть вÑе" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "ПоиÑк файлов" +msgstr "Сортировать файлы" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Сортировать по имени (по возраÑтанию)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Сортировать по имени (по убыванию)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Сортировать по типу (по возраÑтанию)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Сортировать по типу (по убыванию)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "ПоÑледнее изменение" +msgstr "Сортировать по поÑледнему изменению" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "ПоÑледнее изменение" +msgstr "Сортировать по первому изменению" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4162,7 +4159,7 @@ msgstr "Переименовать..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "СфокуÑироватьÑÑ Ð½Ð° поле поиÑка" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4470,14 +4467,12 @@ msgid "Failed to load resource." msgstr "Ðе удалоÑÑŒ загрузить реÑурÑ." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "СвойÑтва" +msgstr "Копировать ÑвойÑтва" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "СвойÑтва" +msgstr "Ð’Ñтавить ÑвойÑтва" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4502,23 +4497,20 @@ msgid "Save As..." msgstr "Сохранить как..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Ðе в пути реÑурÑов." +msgstr "Дополнительные параметры реÑурÑа." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Редактировать реÑÑƒÑ€Ñ Ð² буфере обмена" +msgstr "Редактировать реÑÑƒÑ€Ñ Ð¸Ð· буфера обмена" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Копировать параметры" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Сделать вÑтроенным" +msgstr "Сделать реÑÑƒÑ€Ñ Ð²Ñтроенным" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4533,9 +4525,8 @@ msgid "History of recently edited objects." msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½ÐµÐ´Ð°Ð²Ð½Ð¾ отредактированных объектов." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Открыть документацию" +msgstr "Открыть документацию Ð´Ð»Ñ Ñтого объекта." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4546,9 +4537,8 @@ msgid "Filter properties" msgstr "Фильтр ÑвойÑтв" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "СвойÑтва объекта." +msgstr "Управление ÑвойÑтвами объекта." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4793,9 +4783,8 @@ msgid "Blend:" msgstr "Смешивание:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Параметр изменён" +msgstr "Параметр изменён:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5525,11 +5514,11 @@ msgstr "Ð’Ñе" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "ПоиÑк шаблонов, проектов и демо" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "ПоиÑк реÑурÑов (иÑÐºÐ»ÑŽÑ‡Ð°Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ‹, проекты и демо)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5573,7 +5562,7 @@ msgstr "ZIP файл аÑÑетов" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Ðудио превью Старт/Пауза" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5830,13 +5819,12 @@ msgstr "Изменить привÑзку" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" "Переопределение игровой камеры\n" -"ПереопределÑет игровую камеру камерой редактора viewport." +"ПереопределÑет игровую камеру запущенного проекта камерой viewport редактора." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5845,6 +5833,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Переопределение игровой камеры\n" +"ÐкземплÑÑ€ проекта не запущен. ЗапуÑтите проект из редактора, чтобы " +"иÑпользовать Ñту функцию." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5912,31 +5903,27 @@ msgstr "Режим выделениÑ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Удалить выделенный узел или переход." +msgstr "Тащить: Вращать выделенный узел вокруг pivot." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Тащить: Перемещение" +msgstr "Alt+Тащить: перемещение выбранного узла." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Удалить выделенный узел или переход." +msgstr "V: Задать положение pivot выделенного узла." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Показывает ÑпиÑок вÑех объектов нажатой позиции,\n" -"(так же как и Alt+ПКМ в режиме выделениÑ)." +"Alt+ПКМ: Показать ÑпиÑок вÑех узлов в выбранной позиции, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ " +"заблокированные." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "ПКМ: Добавить узел в выбранной позиции." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6174,14 +6161,12 @@ msgid "Clear Pose" msgstr "ОчиÑтить позу" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Добавить узел" +msgstr "Добавить узел Ñюда" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Дополнить Ñценой(ами)" +msgstr "ИнÑтанцировать Ñцену Ñюда" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6197,49 +6182,43 @@ msgstr "Панорамировать вид" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "МаÑштаб 3,125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "МаÑштаб 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "МаÑштаб 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Отдалить" +msgstr "МаÑштаб 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Отдалить" +msgstr "МаÑштаб 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Отдалить" +msgstr "МаÑштаб 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Отдалить" +msgstr "МаÑштаб 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Отдалить" +msgstr "МаÑштаб 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Отдалить" +msgstr "МаÑштаб 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "МаÑштаб 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6484,9 +6463,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Ðе удалоÑÑŒ Ñоздать ни одной выпуклой формы ÑтолкновениÑ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Создать одну выпуклую форму" +msgstr "Создать упрощённую выпуклую форму" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6522,9 +6500,8 @@ msgid "No mesh to debug." msgstr "Ðет полиÑетки Ð´Ð»Ñ Ð¾Ñ‚Ð»Ð°Ð´ÐºÐ¸." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "У модели нет UV в Ñтом Ñлое" +msgstr "ПолиÑетка не имеет UV в Ñлое %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6589,9 +6566,8 @@ msgstr "" "Ðто Ñамый быÑтрый (но наименее точный) ÑпоÑоб Ð¾Ð±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñтолкновений." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Создать одну выпуклую облаÑть ÑтолкновениÑ" +msgstr "Создать ÑоÑеднюю упрощённую выпуклую коллизию" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6599,20 +6575,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Создает упрощенную выпуклую форму ÑтолкновениÑ.\n" +"Она похожа на одиночную форму ÑтолкновениÑ, но в некоторых ÑлучаÑÑ… может " +"получитьÑÑ Ð±Ð¾Ð»ÐµÐµ проÑÑ‚Ð°Ñ Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ, ценой ÑÐ½Ð¸Ð¶ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾Ñти." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Создать выпуклую облаÑть ÑтолкновениÑ" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "Создает форму ÑÑ‚Ð¾Ð»ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ Ð½Ð° оÑнове полигона.\n" -"Ðто Ñредний по производительноÑти вариант между Ð´Ð²ÑƒÐ¼Ñ Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð¸Ð¼Ð¸." +"Ðто Ñредний по производительноÑти вариант между одиночным выпуклым " +"Ñтолкновением и Ñтолкновением на оÑнове полигонов." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7255,24 +7234,20 @@ msgid "ResourcePreloader" msgstr "Предзагрузчик реÑурÑов" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Перевернуть по горизонтали" +msgstr "Перевернуть порталы" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "КоличеÑтво Ñоздаваемых точек:" +msgstr "Точки генерации комнаты" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "КоличеÑтво Ñоздаваемых точек:" +msgstr "Генерировать точки" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Перевернуть по горизонтали" +msgstr "Перевернуть портал" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7836,20 +7811,17 @@ msgid "None" msgstr "ПуÑто" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "ГоÑударÑтво" +msgstr "Повернуть" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Перемещение:" +msgstr "Сдвинуть" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "МаÑштаб:" +msgstr "МаÑштабировать" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7872,52 +7844,44 @@ msgid "Animation Key Inserted." msgstr "Ключ анимации вÑтавлен." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Ð’Ñ‹Ñота" +msgstr "Ð’Ñ‹Ñота:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Ð Ñ‹Ñкание:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Размер: " +msgstr "Размер:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "ÐариÑовано объектов" +msgstr "ОтриÑовано объектов:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð°Ñ‚ÐµÑ€Ð¸Ð°Ð»Ð°" +msgstr "Материалов изменено:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑˆÐµÐ¹Ð´ÐµÑ€Ð¾Ð²" +msgstr "Шейдеров изменено:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð²ÐµÑ€Ñ…Ð½Ð¾Ñти" +msgstr "ПоверхноÑтей изменено:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Вызовы отриÑовки" +msgstr "Вызовов отриÑовки:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Вершины" +msgstr "Вершины:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS: %d (%s мÑ)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8072,9 +8036,8 @@ msgid "Freelook Slow Modifier" msgstr "Модификатор Ð·Ð°Ð¼ÐµÐ´Ð»ÐµÐ½Ð¸Ñ Ñвободного вида" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Изменить размер камеры" +msgstr "Переключить превью камеры" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8097,9 +8060,8 @@ msgstr "" "игры." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Преобразовать в %s" +msgstr "Преобразовать комнаты" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8121,7 +8083,6 @@ msgstr "" "(«рентген»)." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "ПривÑзать узлы к полу" @@ -8139,7 +8100,7 @@ msgstr "ИÑпользовать привÑзки" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Преобразует комнаты Ð´Ð»Ñ portal culling." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8235,9 +8196,8 @@ msgid "View Grid" msgstr "Отображать Ñетку" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "ÐаÑтройки окна проÑмотра" +msgstr "Отображать portal culling" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8559,221 +8519,196 @@ msgid "TextureRegion" msgstr "ОблаÑть текÑтуры" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Цвет" +msgstr "Цвета" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Шрифт" +msgstr "Шрифты" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Иконка" +msgstr "Иконки" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Стиль" +msgstr "Стили" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} цвет(ов)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Вложенные реÑурÑÑ‹ не найдены." +msgstr "Цвета не найдены." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "КонÑтанты" +msgstr "{num} конÑтанта(Ñ‹)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Ð¦Ð²ÐµÑ‚Ð¾Ð²Ð°Ñ ÐºÐ¾Ð½Ñтанта." +msgstr "КонÑтанты не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} шрифт(ов)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Ðе найдено!" +msgstr "Шрифты не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} иконка(ок)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Ðе найдено!" +msgstr "Иконки не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} Ñтиль(ей)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Вложенные реÑурÑÑ‹ не найдены." +msgstr "Стили не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} выбрано в данный момент" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Ð”Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° ничего не выбрано." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Импортировать тему" +msgstr "Импортировать Ñлементы темы" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Импорт Ñлементов {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Выйти из редактора?" +msgstr "Обновление редактора" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Ðнализ" +msgstr "Завершение" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Фильтр: " +msgstr "Фильтр:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "С данными" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Выбрать узел" +msgstr "Выбрать по типу данных:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Выберите разделение, чтобы Ñтереть его." +msgstr "Выбрать вÑе видимые цвета." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Выделить вÑе видимые цвета и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых цветов." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые конÑтанты." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Выделить вÑе видимые конÑтанты и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых конÑтант." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые шрифты." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Выделить вÑе видимые шрифты и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых шрифтов." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые иконки." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые иконки и их данные." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "СнÑть выделение Ñо вÑех видимых иконок." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Выделить вÑе видимые Ñтили." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Выделить вÑе видимые Ñтили и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых Ñтилей." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Внимание: Добавление данных об иконках может значительно увеличить размер " +"реÑурÑа вашей темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Свернуть вÑе" +msgstr "Свернуть типы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Развернуть вÑе" +msgstr "Развернуть типы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Выбрать файл шаблона" +msgstr "Выбрать вÑе Ñлементы темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Выбрать точки" +msgstr "Выделить Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Выделить вÑе Ñлементы темы Ñ Ð¸Ñ… данными." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Выделить вÑÑ‘" +msgstr "СнÑть выделение Ñо вÑего" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех Ñлементов темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Импортировать Ñцену" +msgstr "Импортировать выделенное" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8781,271 +8716,248 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"Ðа вкладке «Импорт Ñлементов» выбраны некоторые Ñлементы. При закрытии Ñтого " +"окна выбор будет потерÑн.\n" +"Ð’ÑÑ‘ равно закрыть?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе цвета" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Удалить Ñлемент" +msgstr "Переименовать Ñлемент" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе конÑтанты" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе шрифты" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе иконки" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе Ñтили" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Добавить Ñлемент клаÑÑа" +msgstr "Добавить цвет" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Добавить Ñлемент клаÑÑа" +msgstr "Добавить конÑтанту" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Добавить Ñлемент" +msgstr "Добавить шрифт" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Добавить Ñлемент" +msgstr "Добавить иконку" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Добавить вÑе Ñлементы" +msgstr "Добавить Ñтиль" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Удалить Ñлемент клаÑÑа" +msgstr "Переименовать цвет" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Удалить Ñлемент клаÑÑа" +msgstr "Переименовать конÑтанту" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Переименовать узел" +msgstr "Переименовать шрифт" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Переименовать узел" +msgstr "Переименовать иконку" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Удалить выбранный Ñлемент" +msgstr "Удалить Ñтиль" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "ÐедопуÑтимый файл, не раÑкладка аудио шины." +msgstr "Ðеверный файл, не реÑÑƒÑ€Ñ Ñ‚ÐµÐ¼Ñ‹." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "ÐедопуÑтимый файл, так же, как и редактируемый реÑÑƒÑ€Ñ Ñ‚ÐµÐ¼Ñ‹." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Управление шаблонами" +msgstr "Управление Ñлементами темы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Редактируемый Ñлемент" +msgstr "Редактировать Ñлементы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Тип:" +msgstr "Типы:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Тип:" +msgstr "Добавить тип:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Добавить Ñлемент" +msgstr "Добавить Ñлемент:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Добавить вÑе Ñлементы" +msgstr "Добавить Ñтиль" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Удалить Ñлемент" +msgstr "Удалить Ñлементы:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" msgstr "Удалить Ñлемент клаÑÑа" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Удалить Ñлемент клаÑÑа" +msgstr "Удалить пользовательÑкие Ñлементы" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "Удалить вÑе Ñлементы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Тема Ñлементов GUI" +msgstr "Добавить Ñлемент темы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Ð˜Ð¼Ñ ÑƒÐ·Ð»Ð°:" +msgstr "Старое имÑ:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Импортировать тему" +msgstr "Импортировать Ñлементы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "По умолчанию" +msgstr "Тема по умолчанию" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Редактировать тему" +msgstr "Тема редактора" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Удалить реÑурÑ" +msgstr "Выбрать другой реÑÑƒÑ€Ñ Ñ‚ÐµÐ¼Ñ‹:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Импортировать тему" +msgstr "Ð”Ñ€ÑƒÐ³Ð°Ñ Ñ‚ÐµÐ¼Ð°" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Переименовать дорожку" +msgstr "Подтвердить переименование Ñлемента" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Групповое переименование" +msgstr "Отменить переименование Ñлемента" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Переопределить" +msgstr "Переопределить Ñлемент" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Открепить Ñтот StyleBox в качеÑтве оÑновного ÑтилÑ." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Закрепить Ñтот StyleBox в качеÑтве оÑновного ÑтилÑ. При редактировании его " +"ÑвойÑтв будут обновлены те же ÑвойÑтва во вÑех других объектах StyleBoxes " +"Ñтого типа." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Тип" +msgstr "Добавить тип" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Добавить Ñлемент" +msgstr "Добавить тип Ñлемента" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Тип узла" +msgstr "Типы узлов:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Загрузить по умолчанию" +msgstr "Показать по умолчанию" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Показывать Ñлементы типа по умолчанию Ñ€Ñдом Ñ Ñлементами, которые были " +"переопределены." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Переопределить" +msgstr "Переопределить вÑÑ‘" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Переопределить вÑе Ñлементы типа по умолчанию." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Тема" +msgstr "Тема:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Управление шаблонами ÑкÑпорта..." +msgstr "Управление Ñлементами..." #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Добавление, удаление, упорÑдочивание и импорт Ñлементов темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "ПредпроÑмотр" +msgstr "Добавить превью" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Обновить предварительный проÑмотр" +msgstr "Превью по умолчанию" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Выберите иÑточник полиÑетки:" +msgstr "Выбрать UI-Ñцену:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Переключает ÑредÑтво выбора Control, позволÑÑ Ð²Ð¸Ð·ÑƒÐ°Ð»ÑŒÐ½Ð¾ выбирать типы " +"Control Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9080,9 +8992,8 @@ msgid "Checked Radio Item" msgstr "Отмеченный переключатель" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Имен. раздел." +msgstr "Именованный разделитель" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9135,19 +9046,20 @@ msgstr "ЕÑть,Много,Вариантов" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"ÐедопуÑтимый путь, реÑÑƒÑ€Ñ PackedScene, вероÑтно, был перемещен или удален." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"ÐедопуÑтимый реÑÑƒÑ€Ñ PackedScene, он должен иметь корневой узел Control." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "ÐедопуÑтимый файл, не раÑкладка аудио шины." +msgstr "Ðеверный файл, не реÑÑƒÑ€Ñ PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Перезагрузите Ñцену, чтобы отразить её наиболее актуальное ÑоÑтоÑние." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10545,9 +10457,8 @@ msgid "VisualShader" msgstr "Визуальный шейдер" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Редактировать визуальное ÑвойÑтво" +msgstr "Редактировать визуальное ÑвойÑтво:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10673,9 +10584,8 @@ msgid "Script" msgstr "Скрипт" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Режим ÑкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñкриптов:" +msgstr "Режим ÑкÑпорта GDScript:" #: editor/project_export.cpp msgid "Text" @@ -10683,22 +10593,20 @@ msgstr "ТекÑтовый" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Скомпилированный байткод (более быÑÑ‚Ñ€Ð°Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ°)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Зашифрованный (Ðапишите ключ ниже)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" msgstr "" -"ÐедейÑтвительный ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (длина ключа должна ÑоÑтавлÑть 64 Ñимвола)" +"Ðеверный ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (должен ÑоÑтоÑть из 64 шеÑтнадцатеричных Ñимволов)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñкрипта (256-бит, в шеÑтнадцатеричном виде):" +msgstr "Ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ GDScript (256 бит, в шеÑтнадцатеричном виде):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10770,7 +10678,6 @@ msgid "Imported Project" msgstr "Импортированный проект" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "ÐедопуÑтимое Ð¸Ð¼Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°." @@ -10996,14 +10903,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Ð’Ñ‹ уверены, что хотите запуÑтить %d проектов одновременно?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Выберите уÑтройÑтво из ÑпиÑка" +msgstr "Удалить %d проектов из ÑпиÑка?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Выберите уÑтройÑтво из ÑпиÑка" +msgstr "Удалить Ñтот проект из ÑпиÑка?" #: editor/project_manager.cpp msgid "" @@ -11035,9 +10940,8 @@ msgid "Project Manager" msgstr "Менеджер проектов" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Проекты" +msgstr "Локальные проекты" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11048,23 +10952,20 @@ msgid "Last Modified" msgstr "ПоÑледнее изменение" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "ÐкÑпортировать проект" +msgstr "Редактировать проект" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Переименовать проект" +msgstr "ЗапуÑтить проект" #: editor/project_manager.cpp msgid "Scan" msgstr "Сканировать" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Проекты" +msgstr "Сканировать проекты" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11075,12 +10976,10 @@ msgid "New Project" msgstr "Ðовый проект" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Импортированный проект" +msgstr "Импортировать проект" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" msgstr "Переименовать проект" @@ -11093,9 +10992,8 @@ msgid "About" msgstr "О Godot Engine" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Библиотека реÑурÑов" +msgstr "Проекты Библиотеки реÑурÑов" #: editor/project_manager.cpp msgid "Restart Now" @@ -11107,7 +11005,7 @@ msgstr "Удалить вÑе" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Также удалить Ñодержимое проекта (Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11122,18 +11020,16 @@ msgstr "" "Хотите изучить официальные примеры в Библиотеке реÑурÑов?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Фильтр ÑвойÑтв" +msgstr "Фильтр проектов" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"Поле поиÑка фильтрует проекты по имени и поÑледнему компоненту пути\n" +"Ðто поле фильтрует проекты по имени и поÑледнему компоненту пути.\n" "Чтобы отфильтровать проекты по имени и полному пути, Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ Ñодержать " "Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñимвол `/`." @@ -11143,7 +11039,7 @@ msgstr "Клавиша " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "ФизичеÑÐºÐ°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ°" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11191,7 +11087,7 @@ msgstr "УÑтройÑтво" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (ФизичеÑкаÑ)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11334,23 +11230,20 @@ msgid "Override for Feature" msgstr "Переопределение ÑвойÑтва" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Добавить перевод" +msgstr "Добавить %d переводов" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Удалить перевод" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Перенаправлен реÑÑƒÑ€Ñ Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ" +msgstr "Переназначение реÑурÑов перевода: Добавить %d путь(ей)" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Перенаправлен реÑÑƒÑ€Ñ Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ" +msgstr "Переназначение реÑурÑов перевода: Добавить %d переназначение(ий)" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11766,7 +11659,7 @@ msgstr "Узел должен принадлежать редактируемоР#: editor/scene_tree_dock.cpp msgid "Instantiated scenes can't become root" -msgstr "ИнÑтанцированные Ñцены не могут Ñтать корневыми" +msgstr "ИнÑтанцированные Ñцены не могут Ñтать корнем" #: editor/scene_tree_dock.cpp msgid "Make node as Root" @@ -11796,12 +11689,16 @@ msgstr "Удалить узел «%s»?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²ÐµÑ‚ÐºÐ¸ как Ñцены необходимо, чтобы Ñцена была открыта в " +"редакторе." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²ÐµÑ‚Ð²Ð¸ как Ñцены требуетÑÑ Ð²Ñ‹Ð±Ñ€Ð°Ñ‚ÑŒ только один узел, но вы " +"выбрали %d узлов." #: editor/scene_tree_dock.cpp msgid "" @@ -11810,6 +11707,12 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðевозможно Ñохранить ветку корневого узла в качеÑтве инÑтанцированной " +"Ñцены.\n" +"Чтобы Ñоздать редактируемую копию текущей Ñцены, продублируйте её Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ " +"контекÑтного меню панели Â«Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема»\n" +"или Ñоздайте унаÑледованную Ñцену, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð²Ð¼ÐµÑто Ñтого Сцена > ÐÐ¾Ð²Ð°Ñ " +"унаÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñцена..." #: editor/scene_tree_dock.cpp msgid "" @@ -11817,6 +11720,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðевозможно Ñохранить ветку уже инÑтанцированной Ñцены.\n" +"Чтобы Ñоздать вариант Ñцены, вы можете Ñоздать унаÑледованную Ñцену на " +"оÑнове инÑтанцированной Ñцены, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð²Ð¼ÐµÑто Ñтого Сцена > ÐÐ¾Ð²Ð°Ñ " +"унаÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñцена..." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12225,6 +12132,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Предупреждение: Обычно нежелательно, чтобы Ð¸Ð¼Ñ Ñкрипта Ñовпадало Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ " +"вÑтроенного типа." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12296,7 +12205,7 @@ msgstr "Копировать ошибку" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Открытый иÑходный код C++ на GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12475,6 +12384,16 @@ msgstr "Изменить выÑоту цилиндра" msgid "Change Ray Shape Length" msgstr "Изменить длину луча" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "УÑтановить положение точки кривой" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "УÑтановить положение точки кривой" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Изменить Ñ€Ð°Ð´Ð¸ÑƒÑ Ñ†Ð¸Ð»Ð¸Ð½Ð´Ñ€Ð°" @@ -12585,14 +12504,12 @@ msgid "Object can't provide a length." msgstr "Объект не может предоÑтавить длину." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "ÐкÑпортировать библиотеку полиÑеток" +msgstr "ÐкÑпорт полиÑетки GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "ÐкÑпортировать..." +msgstr "ÐкÑпорт GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12624,20 +12541,19 @@ msgstr "Удалить выделенную Ñетку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Fill Selection" -msgstr "Залить выделенную GridMap" +msgstr "Залить выделенную Ñетку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Paste Selection" -msgstr "Ð’Ñтавка выделенной Ñетки" +msgstr "Ð’Ñтавить выделенную Ñетку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Paint" msgstr "РиÑование Ñетки" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Залить выделенную GridMap" +msgstr "Выделение Ñетки" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12653,11 +12569,11 @@ msgstr "Отключить обрезку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Above" -msgstr "Отрезать Ñверху" +msgstr "Обрезать Ñверху" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Below" -msgstr "Отрезать Ñнизу" +msgstr "Обрезать Ñнизу" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit X Axis" @@ -12889,14 +12805,12 @@ msgid "Add Output Port" msgstr "Добавить выходной порт" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Изменить тип" +msgstr "Изменить тип порта" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Изменить Ð¸Ð¼Ñ Ð²Ñ…Ð¾Ð´Ð½Ð¾Ð³Ð¾ порта" +msgstr "Изменить Ð¸Ð¼Ñ Ð¿Ð¾Ñ€Ñ‚Ð°" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13011,9 +12925,8 @@ msgid "Add Preload Node" msgstr "Добавить предзагрузочный узел" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Добавить узел" +msgstr "Добавить узел(узлы)" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13276,37 +13189,31 @@ msgstr "Выберите уÑтройÑтво из ÑпиÑка" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "ВыполнÑетÑÑ Ð½Ð° %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "ÐкÑпорт вÑех" +msgstr "ÐкÑпорт APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Удалить" +msgstr "Удаление..." #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Загрузка, пожалуйÑта, ждите..." +msgstr "УÑтановка на уÑтройÑтво, пожалуйÑта, ждите..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Ðе возможно добавить Ñцену!" +msgstr "Ðе удалоÑÑŒ уÑтановить на уÑтройÑтво: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "ЗапуÑк пользовательÑкого Ñкрипта..." +msgstr "ЗапуÑк на уÑтройÑтве..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Ðевозможно Ñоздать папку." +msgstr "Ðе удалоÑÑŒ выполнить на уÑтройÑтве." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13431,40 +13338,37 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"Ðе удалоÑÑŒ найти команду «apksigner».\n" +"ПожалуйÑта, проверьте наличие программы в каталоге Android SDK build-tools.\n" +"Результат %s не подпиÑан." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "ПодпиÑание отладочного %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Сканирование файлов,\n" -"пожалуйÑта, ждите..." +msgstr "ПодпиÑание релиза %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Ðе удалоÑÑŒ открыть шаблон Ð´Ð»Ñ ÑкÑпорта:" +msgstr "Ðе удалоÑÑŒ найти хранилище ключей, невозможно ÑкÑпортировать." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "«apksigner» завершилÑÑ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹ #%d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "Добавление %s..." +msgstr "Проверка %s..." #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "Проверка «apksigner» «%s» не удалаÑÑŒ." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "ÐкÑпорт вÑех" +msgstr "ÐкÑпорт Ð´Ð»Ñ Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13480,7 +13384,7 @@ msgstr "Ðеверное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°! Android APK требует раÑшР#: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Ðеподдерживаемый формат ÑкÑпорта!\n" #: platform/android/export/export.cpp msgid "" @@ -13506,16 +13410,15 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Ðевозможно перезапиÑать файлы res://android/build/res/*.xml Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ проекта" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "ОтÑутÑтвует project.godot в папке проекта." +msgstr "Ðе удалоÑÑŒ ÑкÑпортировать файлы проекта в проект gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Ðе удалоÑÑŒ запиÑать файл:" +msgstr "Ðе удалоÑÑŒ запиÑать раÑширение файла пакета!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13543,21 +13446,20 @@ msgstr "" "проекта gradle на наличие выходных данных." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ Ð½Ðµ найдена: %s" +msgstr "Пакет не найден: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Создание контуров..." +msgstr "Создание APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Ðе удалоÑÑŒ открыть шаблон Ð´Ð»Ñ ÑкÑпорта:" +msgstr "" +"Ðе удалоÑÑŒ найти шаблон APK Ð´Ð»Ñ ÑкÑпорта:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13566,16 +13468,17 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Ð’ шаблоне ÑкÑпорта отÑутÑтвуют библиотеки Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… архитектур: %s.\n" +"ПожалуйÑта, Ñоздайте шаблон Ñо вÑеми необходимыми библиотеками или Ñнимите " +"флажки Ñ Ð¾Ñ‚ÑутÑтвующих архитектур в преÑете ÑкÑпорта." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "Добавление %s..." +msgstr "Добавление файлов..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Ðе удалоÑÑŒ запиÑать файл:" +msgstr "Ðе удалоÑÑŒ ÑкÑпортировать файлы проекта" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13583,7 +13486,7 @@ msgstr "Выравнивание APK..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Ðе удалоÑÑŒ раÑпаковать временный невыровненный APK." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13630,45 +13533,40 @@ msgid "Could not write file:" msgstr "Ðе удалоÑÑŒ запиÑать файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Ðе удалоÑÑŒ запиÑать файл:" +msgstr "Ðе удалоÑÑŒ прочитать файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "Ðе удалоÑÑŒ прочитать пользовательÑкую HTML оболочку:" +msgstr "Ðе удалоÑÑŒ прочитать HTML-оболочку:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Ðевозможно Ñоздать папку." +msgstr "Ðе удалоÑÑŒ Ñоздать каталог HTTP-Ñервера:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñцены." +msgstr "Ошибка запуÑка HTTP-Ñервера:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Ðеверный идентификатор:" +msgstr "Ðеверный идентификатор пакета:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "Предупреждение: требуетÑÑ Ð¿Ð¾Ð´Ð¿Ð¸Ñание кода." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "Предупреждение: требуетÑÑ ÑƒÑиленный рантайм." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "Предупреждение: Ð¸Ð¼Ñ Apple ID не указано." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "Предупреждение: пароль Apple ID не указан." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14102,6 +14000,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"СвойÑтво GIProbe Compress было объÑвлено уÑтаревшим из-за извеÑтных ошибок и " +"больше не имеет никакого Ñффекта.\n" +"Чтобы убрать Ñто предупреждение, отключите ÑвойÑтво Compress в GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14187,15 +14088,15 @@ msgstr "Узел Ри Узел Ð’ должны быть различными о #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomManager не должен быть ребёнком или внуком Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Room не должен быть ребёнком или внуком Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomGroup не должен быть ребёнком или внуком Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14207,42 +14108,83 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." -msgstr "" +msgstr "Room не должен быть ребёнком или внуком другого Room." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager не должен раÑполагатьÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Room." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup не должен раÑполагатьÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Room." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"Выпуклый ÐºÐ¾Ñ€Ð¿ÑƒÑ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ñ‹ Ñодержит большое количеÑтво плоÑкоÑтей.\n" +"РаÑÑмотрите возможноÑть ÑƒÐ¿Ñ€Ð¾Ñ‰ÐµÐ½Ð¸Ñ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ñ‹ комнаты Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‹ÑˆÐµÐ½Ð¸Ñ " +"производительноÑти." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager не должен раÑполагатьÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ RoomGroup." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList не был назначен." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "Узел RoomList должен быть Spatial (или унаÑледован от Spatial)." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Portal Depth Limit уÑтановлено на ноль.\n" +"Будет отриÑовыватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ комната, в которой находитÑÑ ÐºÐ°Ð¼ÐµÑ€Ð°." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "Ð’ SceneTree должен быть только один RoomManager." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14309,7 +14251,7 @@ msgstr "ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ Ð½Ðµ найдена: %s" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ - Применить ÑброÑ" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14484,25 +14426,28 @@ msgid "Invalid comparison function for that type." msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтого типа." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ быть назначены только в функции вершины." +msgstr "Varying не может быть задано в функции «%s»." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Varying, заданные в функции «vertex», не могут быть изменены в «fragment» " +"или «light»." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Varying, заданные в функции «fragment», не могут быть изменены в «vertex» " +"или «light»." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" -msgstr "" +msgstr "Varying Ñтадии fragment не доÑтупны из пользовательÑких функций!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15507,9 +15452,6 @@ msgstr "КонÑтанты не могут быть изменены." #~ msgid "I see..." #~ msgstr "ЯÑно..." -#~ msgid "Can't open '%s'." -#~ msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ '%s'." - #~ msgid "Ugh" #~ msgstr "ЯÑно" diff --git a/editor/translations/si.po b/editor/translations/si.po index 94841d0879..595e0041a9 100644 --- a/editor/translations/si.po +++ b/editor/translations/si.po @@ -348,6 +348,7 @@ msgstr "සජීවීකරණ පුනරà·à·€à¶»à·Šà¶®à¶±à¶º" msgid "Remove Anim Track" msgstr "Anim ලුහුබදින්න෠ඉවà¶à·Š කරන්න" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s සදහ෠නව ලුහුබදින්නෙà¶à·Š à·ƒà·à¶¯à· යà¶à·”රක් ඇà¶à·”à¶½à¶à·Š කරන්න?" @@ -372,10 +373,27 @@ msgstr "à·ƒà·à¶¯à¶±à·Šà¶±" msgid "Anim Insert" msgstr "Anim ඇà¶à·”à¶½à¶à·Š කරන්න" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "à·à·Šâ€à¶»à·’à¶:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "සජීවීකරණ à¶°à·à·€à¶šà¶º à¶à¶¸à·à¶§à¶¸ සජීවීකරණය à¶šà¶½ නොහà·à¶š, අනෙක් à¶°à·à·€à¶š පමණි." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ලක්ෂණය ලුහුබදින්න" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim à·ƒà·à¶¯à¶±à·Šà¶± සහ ඇà¶à·”à¶½à¶à·Š කරන්න" @@ -942,7 +960,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2237,6 +2255,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3017,10 +3046,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "අඛණ්ඩව" @@ -3627,6 +3652,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8341,6 +8374,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8367,6 +8406,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11809,6 +11854,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13418,6 +13471,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sk.po b/editor/translations/sk.po index 6deda77711..54736cff85 100644 --- a/editor/translations/sk.po +++ b/editor/translations/sk.po @@ -346,6 +346,7 @@ msgstr "ZmeniÅ¥ Loop Mode Animacie" msgid "Remove Anim Track" msgstr "VymazaÅ¥ Track Animácie" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "VytvoriÅ¥ NOVà track za %s a vložiÅ¥ kľúÄ?" @@ -370,10 +371,27 @@ msgstr "VytvoriÅ¥" msgid "Anim Insert" msgstr "Animácia VložiÅ¥" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animácie" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nemôže animovaÅ¥ sám seba, iba ostatný hráÄi." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "VlastnosÅ¥" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animácia VytvoriÅ¥ & VložiÅ¥" @@ -945,7 +963,7 @@ msgstr "VytvoriÅ¥ Nový %s" msgid "No results for \"%s\"." msgstr "Žiadne výsledky pre \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2295,6 +2313,17 @@ msgid "New Window" msgstr "Nové Okno" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "OtáÄa sa, keÄ sa okno editora redistribuuje." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importované zdroje nemôžu byÅ¥ uložené." @@ -3150,10 +3179,6 @@ msgid "Save & Restart" msgstr "UložiÅ¥ & ReÅ¡tartovaÅ¥" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "OtáÄa sa, keÄ sa okno editora redistribuuje." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "AktualizovaÅ¥ priebežne" @@ -3812,6 +3837,15 @@ msgid "Download from:" msgstr "Chyba SÅ¥ahovania" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "OtvoriÅ¥ v File Manažérovy" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8677,6 +8711,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "VÅ¡etky vybrané" @@ -8707,6 +8747,12 @@ msgid "Remove All StyleBox Items" msgstr "VÅ¡etky vybrané" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "PridaÅ¥ do Obľúbených" @@ -12299,6 +12345,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "VÅ¡etky vybrané" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "VÅ¡etky vybrané" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13978,6 +14034,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sl.po b/editor/translations/sl.po index 53b4bca499..725f88f0ab 100644 --- a/editor/translations/sl.po +++ b/editor/translations/sl.po @@ -369,6 +369,7 @@ msgstr "Spremeni Zanko Animacije" msgid "Remove Anim Track" msgstr "Odstrani animacijsko sled" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Ustvarim NOVO sled za %s in vstavi kljuÄ?" @@ -393,10 +394,28 @@ msgstr "Ustvari" msgid "Anim Insert" msgstr "Vstavi Animacijo" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "NaÄin Zaskoka (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacija" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Lastnosti" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Ustvari & Vstavi Animacijo" @@ -998,7 +1017,7 @@ msgstr "Ustvari Nov %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2397,6 +2416,18 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "Vrti se ob spremembi okna urejevalnika!" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3270,11 +3301,6 @@ msgstr "Shrani & Zapri" #: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "Vrti se ob spremembi okna urejevalnika!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "Neprekinjeno" @@ -3922,6 +3948,15 @@ msgid "Download from:" msgstr "Napaka Pri Prenosu" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Pokaži V Upravitelju Datotek" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8959,6 +8994,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Odstrani Vse Stvari" @@ -8989,6 +9030,12 @@ msgid "Remove All StyleBox Items" msgstr "Odstrani Vse Stvari" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Priljubljene:" @@ -12643,6 +12690,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Nastavi Položaj Krivuljne ToÄke" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Nastavi Položaj Krivuljne ToÄke" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14339,6 +14396,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14856,9 +14949,6 @@ msgstr "Konstante ni možno spreminjati." #~ msgid "Scale Mode (R)" #~ msgstr "NaÄin Obsega (R)" -#~ msgid "Snap Mode (%s)" -#~ msgstr "NaÄin Zaskoka (%s)" - #~ msgid "Tool Select" #~ msgstr "Izbira Orodja" diff --git a/editor/translations/sq.po b/editor/translations/sq.po index f843d97c4e..ded08d5532 100644 --- a/editor/translations/sq.po +++ b/editor/translations/sq.po @@ -342,6 +342,7 @@ msgstr "Ndrysho Metodën e Përsëritjes së Animacionit" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -366,10 +367,27 @@ msgstr "Krijo" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacionet:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Vetitë:" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -938,7 +956,7 @@ msgstr "Krijo %s të ri" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2330,6 +2348,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Rrotullohet kur dritarja e editorit rivizaton." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Resurset e importuara nuk mund të ruhen." @@ -3207,10 +3236,6 @@ msgid "Save & Restart" msgstr "Ruaj & Rifillo" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Rrotullohet kur dritarja e editorit rivizaton." - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "I Vazhdueshëm" @@ -3863,6 +3888,15 @@ msgid "Download from:" msgstr "Shkarko" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Hap në Menaxherin e Skedarëve" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8684,6 +8718,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Hiq Autoload-in" @@ -8714,6 +8754,12 @@ msgid "Remove All StyleBox Items" msgstr "Hiq Autoload-in" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Shto te të preferuarat" @@ -12247,6 +12293,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13891,6 +13945,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sr_Cyrl.po b/editor/translations/sr_Cyrl.po index f95781bd70..0a915e03bf 100644 --- a/editor/translations/sr_Cyrl.po +++ b/editor/translations/sr_Cyrl.po @@ -382,6 +382,7 @@ msgstr "Промени Ñ†Ð¸ÐºÐ»ÑƒÑ Ð°Ð½Ð¸Ð¼Ð°Ñ†Ð¸Ñ˜Ðµ" msgid "Remove Anim Track" msgstr "Обриши траку анимације" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Ðаправите нову траку за %s и убаците кључ?" @@ -406,11 +407,29 @@ msgstr "Ðаправи" msgid "Anim Insert" msgstr "Ðалепи" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Режим лепљења:" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Ðнимација" + #: editor/animation_track_editor.cpp #, fuzzy msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer не може Ñам Ñебе да анимира, Ñамо друге плејере." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ОÑобина %s' не поÑтоји." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Ðаправи анимацију и убаци" @@ -1051,7 +1070,7 @@ msgstr "Ðаправи нов" msgid "No results for \"%s\"." msgstr "Ðема резултата за \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2502,6 +2521,18 @@ msgid "New Window" msgstr "Ðов Прозор" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "Окрене Ñе кад Ñе едиторÑки прозор поново обоји!" + +#: editor/editor_node.cpp #, fuzzy msgid "Imported resources can't be saved." msgstr "Увезени реÑурÑи не могу бити упамћени." @@ -3397,11 +3428,6 @@ msgstr "Сачувај и изађи" #: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "Окрене Ñе кад Ñе едиторÑки прозор поново обоји!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "Трајан" @@ -4106,6 +4132,16 @@ msgid "Download from:" msgstr "Грешка при преузимању" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Покрени у Претраживачу" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Копирај Грешку" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -9423,6 +9459,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Обриши Ñве Ñтавке" @@ -9453,6 +9495,12 @@ msgid "Remove All StyleBox Items" msgstr "Обриши Ñве Ñтавке" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Додај Ñтавке клаÑе" @@ -13751,6 +13799,16 @@ msgstr "Промени ВиÑину Цилиндар Облика" msgid "Change Ray Shape Length" msgstr "Промени Дужину Зрак Облика" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ПоÑтави позицију тачке криве" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ПоÑтави позицију тачке криве" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -15695,6 +15753,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp #, fuzzy msgid "This body will be ignored until you set a mesh." @@ -16456,10 +16550,6 @@ msgstr "КонÑтанте није могуће мењати." #~ msgid "Local Coords" #~ msgstr "Локалне координате" -#, fuzzy -#~ msgid "Snap Mode (%s)" -#~ msgstr "Режим лепљења:" - #~ msgid "Tool Select" #~ msgstr "Избор алатки" diff --git a/editor/translations/sr_Latn.po b/editor/translations/sr_Latn.po index 877149b6ea..76982c0b00 100644 --- a/editor/translations/sr_Latn.po +++ b/editor/translations/sr_Latn.po @@ -356,6 +356,7 @@ msgstr "Optimizuj Animaciju" msgid "Remove Anim Track" msgstr "Odstrani Kanal Animacije" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Napravi Novi kanal za %s i dodaj kljuÄ?" @@ -380,10 +381,26 @@ msgstr "Napravi" msgid "Anim Insert" msgstr "Animacija Umetni" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Optimizuj Animaciju" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animacija Napravi i Dodaj" @@ -950,7 +967,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2250,6 +2267,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3033,10 +3061,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Neprekidna" @@ -3646,6 +3670,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8405,6 +8437,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8432,6 +8470,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11915,6 +11959,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13530,6 +13582,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sv.po b/editor/translations/sv.po index 7433664d25..373e3aad36 100644 --- a/editor/translations/sv.po +++ b/editor/translations/sv.po @@ -14,7 +14,7 @@ # Mattias Münster <mattiasmun@gmail.com>, 2019. # Anonymous <noreply@weblate.org>, 2020. # Joakim Lundberg <joakim@joakimlundberg.com>, 2020. -# Kristoffer Grundström <swedishsailfishosuser@tutanota.com>, 2020. +# Kristoffer Grundström <swedishsailfishosuser@tutanota.com>, 2020, 2021. # Jonas Robertsson <jonas.robertsson@posteo.net>, 2020, 2021. # André Andersson <andre.eric.andersson@gmail.com>, 2020. # Andreas Westrell <andreas.westrell@gmail.com>, 2020. @@ -27,8 +27,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-13 06:13+0000\n" -"Last-Translator: Leon <joel.lundborg@gmail.com>\n" +"PO-Revision-Date: 2021-08-10 21:40+0000\n" +"Last-Translator: Kristoffer Grundström <swedishsailfishosuser@tutanota.com>\n" "Language-Team: Swedish <https://hosted.weblate.org/projects/godot-engine/" "godot/sv/>\n" "Language: sv\n" @@ -36,7 +36,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -357,6 +357,7 @@ msgstr "Ändra Animationsslingläge" msgid "Remove Anim Track" msgstr "Ta bort Anim spÃ¥r" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Skapa NYTT spÃ¥r för %s och infoga nyckel?" @@ -381,11 +382,29 @@ msgstr "Skapa" msgid "Anim Insert" msgstr "Anim Infoga" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan inte öppna '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Animationsspelaren kan inte animera sig själv, utan bara andra spelare." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Egenskaper" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Skapa & Infoga" @@ -473,7 +492,7 @@ msgstr "Anim Flytta Nycklar" #: editor/animation_track_editor.cpp #: modules/visual_script/visual_script_editor.cpp msgid "Clipboard is empty!" -msgstr "" +msgstr "Klippbordet är tomt!" #: editor/animation_track_editor.cpp msgid "Paste Tracks" @@ -943,9 +962,8 @@ msgid "Edit..." msgstr "Ändra..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" -msgstr "GÃ¥ Till Metod" +msgstr "GÃ¥ till metod" #: editor/create_dialog.cpp msgid "Change %s Type" @@ -963,9 +981,9 @@ msgstr "Skapa Ny %s" msgid "No results for \"%s\"." msgstr "Inga resultat för \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Ingen beskrivning tillgänglig för %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1075,7 +1093,6 @@ msgstr "" "Du kan hitta de borttagna filerna i systemets papperskorg." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1083,9 +1100,10 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Filerna som tas bort krävs av andra resurser för att de ska fungera.\n" -"Ta bort dem ändÃ¥? (gÃ¥r inte Ã¥ngra)\n" -"Du kan hitta de borttagna filerna i systemets papperskorg." +"Filerna som ska tas bort krävs av andra resurser för att de ska fungera.\n" +"Ta bort dem ändÃ¥? (GÃ¥r inte Ã¥ngra.)\n" +"Beroende pÃ¥ hur ditt filsystem är inställt sÃ¥ kommer filerna antingen " +"flyttas till systemets papperskorg eller tas bort permanent." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1260,9 +1278,8 @@ msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "Fel vid öppning av paketetfil, inte i zip-format." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Existerar Redan)" +msgstr "%s (existerar redan)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" @@ -1282,9 +1299,8 @@ msgid "The following files failed extraction from asset \"%s\":" msgstr "Följande filer misslyckades att packas upp frÃ¥n paketet:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "%d fler filer." +msgstr "(och %s fler filer)" #: editor/editor_asset_installer.cpp #, fuzzy @@ -1534,9 +1550,8 @@ msgid "Can't add autoload:" msgstr "Kunde inte lägga till autoladdning:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Fil existerar inte." +msgstr "%s är en ogiltig genväg. Filen existerar inte." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." @@ -1564,9 +1579,8 @@ msgid "Name" msgstr "Namn" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Variabel" +msgstr "Global variabel" #: editor/editor_data.cpp msgid "Paste Params" @@ -1663,22 +1677,20 @@ msgstr "" "Fallback Enabled'." #: editor/editor_export.cpp -#, fuzzy msgid "" "Target platform requires 'PVRTC' texture compression for GLES2. Enable " "'Import Pvrtc' in Project Settings." msgstr "" -"MÃ¥lplattformen kräver 'ETC' texturkomprimering för GLES2. Aktivera 'Import " -"Etc' i Projektinställningarna." +"MÃ¥lplattformen kräver 'PVRTC'-texturkomprimering för GLES2. Aktivera 'Import " +"Pvrtc' i projektinställningarna." #: editor/editor_export.cpp -#, fuzzy msgid "" "Target platform requires 'ETC2' or 'PVRTC' texture compression for GLES3. " "Enable 'Import Etc 2' or 'Import Pvrtc' in Project Settings." msgstr "" -"MÃ¥lplattformen kräver 'ETC2' texturkomprimering för GLES3. Aktivera 'Import " -"Etc 2' i Projektinställningarna." +"MÃ¥lplattformen kräver 'ETC2' eller 'PVRTC'-texturkomprimering för GLES3. " +"Aktivera 'Import Etc 2' eller 'Import Pvrtc' i projektinställningarna." #: editor/editor_export.cpp msgid "" @@ -1742,11 +1754,11 @@ msgstr "Importera" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "TillÃ¥ter att visa och redigera 3D-scener." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "TillÃ¥ter att redigera skript via den integrerade skript-redigeraren." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." @@ -2341,6 +2353,17 @@ msgid "New Window" msgstr "Nytt Fönster" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importerade resurser kan inte sparas." @@ -3198,10 +3221,6 @@ msgid "Save & Restart" msgstr "Spara & Avsluta" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Kontinuerlig" @@ -3844,6 +3863,16 @@ msgid "Download from:" msgstr "Ladda ner" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Kör i Webbläsare" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Fel" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -6348,7 +6377,7 @@ msgstr "" #: editor/plugins/curve_editor_plugin.cpp msgid "Flat 1" -msgstr "" +msgstr "Platt 1" #: editor/plugins/curve_editor_plugin.cpp editor/property_editor.cpp msgid "Ease In" @@ -8789,6 +8818,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Ta bort Alla" @@ -8819,6 +8854,12 @@ msgid "Remove All StyleBox Items" msgstr "Ta bort Alla" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Lägg till i Favoriter" @@ -12425,6 +12466,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14103,6 +14152,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14878,9 +14963,6 @@ msgstr "" #~ msgid "I see..." #~ msgstr "Jag förstÃ¥r..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan inte öppna '%s'." - #, fuzzy #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/ta.po b/editor/translations/ta.po index 5b4e249318..2ad954b971 100644 --- a/editor/translations/ta.po +++ b/editor/translations/ta.po @@ -355,6 +355,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "அசைவூடà¯à®Ÿà¯ பாதையை நீகà¯à®•à¯" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -379,10 +380,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "மாறà¯à®±à®™à¯à®•ளை இதறà¯à®•௠அமை:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -945,7 +962,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2241,6 +2258,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3022,10 +3050,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3633,6 +3657,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8343,6 +8375,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8370,6 +8408,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11815,6 +11859,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13423,6 +13475,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/te.po b/editor/translations/te.po index 6a4e076543..74998009cd 100644 --- a/editor/translations/te.po +++ b/editor/translations/te.po @@ -338,6 +338,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -362,10 +363,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -920,7 +936,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2211,6 +2227,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2991,10 +3018,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3599,6 +3622,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8276,6 +8307,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8300,6 +8337,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11708,6 +11751,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13302,6 +13353,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/th.po b/editor/translations/th.po index ce5e4952f1..231051313a 100644 --- a/editor/translations/th.po +++ b/editor/translations/th.po @@ -356,6 +356,7 @@ msgstr "เปลี่ยนโหมดà¸à¸²à¸£à¸§à¸™à¸‹à¹‰à¸³à¹à¸à¸™à¸´à¹€ msgid "Remove Anim Track" msgstr "ลบà¹à¸—ร็à¸à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "เพิ่มà¹à¸—ร็à¸à¹ƒà¸«à¸¡à¹ˆà¸ªà¸³à¸«à¸£à¸±à¸š %s à¹à¸¥à¸°à¹€à¸žà¸´à¹ˆà¸¡à¸„ีย์?" @@ -380,10 +381,28 @@ msgstr "สร้าง" msgid "Anim Insert" msgstr "à¹à¸—รà¸à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "เปิด '%s' ไม่ได้" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "ตัวเล่นà¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™à¹„ม่สามารถเล่นà¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™à¸”้วยตัวมันเà¸à¸‡à¹„ด้ เล่นได้เฉพาะตัวเล่นà¸à¸·à¹ˆà¸™à¹€à¸—่านั้น" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ไม่พบคุณสมบัติ '%s'" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "สร้างà¹à¸¥à¸°à¹à¸—รà¸à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" @@ -954,7 +973,7 @@ msgstr "สร้าง %s ใหม่" msgid "No results for \"%s\"." msgstr "ไม่มีผลลัพธ์สำหรับ \"%s\"" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2290,6 +2309,17 @@ msgid "New Window" msgstr "หน้าต่างใหม่" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "หมุนเมื่à¸à¸¡à¸µà¸à¸²à¸£à¸§à¸²à¸”หน้าต่างโปรà¹à¸à¸£à¸¡à¹ƒà¸«à¸¡" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "ทรัพยาà¸à¸£à¸—ี่นำเข้ามา ไม่สามารถบันทึà¸à¹„ด้" @@ -3122,10 +3152,6 @@ msgid "Save & Restart" msgstr "บันทึà¸à¹à¸¥à¸°à¹€à¸£à¸´à¹ˆà¸¡à¹ƒà¸«à¸¡à¹ˆ" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "หมุนเมื่à¸à¸¡à¸µà¸à¸²à¸£à¸§à¸²à¸”หน้าต่างโปรà¹à¸à¸£à¸¡à¹ƒà¸«à¸¡" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "à¸à¸±à¸žà¹€à¸”ทà¸à¸¢à¹ˆà¸²à¸‡à¸•่à¸à¹€à¸™à¸·à¹ˆà¸à¸‡" @@ -3776,6 +3802,16 @@ msgid "Download from:" msgstr "ดาวน์โหลดผิดพลาด" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "รันในเบราเซà¸à¸£à¹Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "คัดลà¸à¸à¸œà¸´à¸”พลาด" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8592,6 +8628,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ลบทั้งหมด" @@ -8622,6 +8664,12 @@ msgid "Remove All StyleBox Items" msgstr "ลบทั้งหมด" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "เพิ่มไà¸à¹€à¸—มคลาส" @@ -12217,6 +12265,16 @@ msgstr "ปรับความสูงทรงà¹à¸„ปซูล" msgid "Change Ray Shape Length" msgstr "ปรับความยาวรังสี" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "à¸à¸³à¸«à¸™à¸”พิà¸à¸±à¸”จุดเส้นโค้ง" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "à¸à¸³à¸«à¸™à¸”พิà¸à¸±à¸”จุดเส้นโค้ง" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ปรับรัศมีทรงà¸à¸£à¸°à¸šà¸à¸" @@ -13909,6 +13967,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "วัตถุนี้จะถูà¸à¸¥à¸°à¹€à¸§à¹‰à¸™à¸ˆà¸™à¸à¸§à¹ˆà¸²à¸ˆà¸°à¸•ั้ง mesh" @@ -15120,9 +15214,6 @@ msgstr "ค่าคงที่ไม่สามารถà¹à¸à¹‰à¹„ขได #~ msgid "I see..." #~ msgstr "ตà¸à¸¥à¸‡..." -#~ msgid "Can't open '%s'." -#~ msgstr "เปิด '%s' ไม่ได้" - #~ msgid "Ugh" #~ msgstr "เà¸à¸à¸°" diff --git a/editor/translations/tr.po b/editor/translations/tr.po index 8a735113cc..69a7ef73a2 100644 --- a/editor/translations/tr.po +++ b/editor/translations/tr.po @@ -395,6 +395,7 @@ msgstr "Animasyon Döngü Kipini DeÄŸiÅŸtir" msgid "Remove Anim Track" msgstr "Animasyon İzini Kaldır" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s için YENİ iz oluÅŸtur ve anahtar gir?" @@ -419,11 +420,29 @@ msgstr "OluÅŸtur" msgid "Anim Insert" msgstr "Animasyon Ekle" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'%s' açılamıyor." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasyon" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Animasyon Oynatıcısı kendisini oynatamaz, sadece diÄŸer oynatıcılar yapabilir." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "'%s' özelliÄŸi mevcut deÄŸil." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animasyon OluÅŸtur & Ekle" @@ -999,7 +1018,7 @@ msgstr "Yeni %s OluÅŸtur" msgid "No results for \"%s\"." msgstr "\"%s\" için sonuç yok." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2350,6 +2369,17 @@ msgid "New Window" msgstr "Yeni Pencere" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Düzenleyici penceresi yeniden boyandığında döner." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "İçe aktarılmış kaynaklar kaydedilemez." @@ -3212,10 +3242,6 @@ msgid "Save & Restart" msgstr "Kaydet ve BaÅŸtan BaÅŸlat" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Düzenleyici penceresi yeniden boyandığında döner." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Sürekli Güncelle" @@ -3878,6 +3904,16 @@ msgid "Download from:" msgstr "İndirme Hatası" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Tarayıcıda Çalıştır" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Hatayı Kopyala" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8740,6 +8776,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Bütün Öğeleri Kaldır" @@ -8770,6 +8812,12 @@ msgid "Remove All StyleBox Items" msgstr "Bütün Öğeleri Kaldır" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Sınıf Öğeleri Ekle" @@ -12426,6 +12474,16 @@ msgstr "Silindir Åžekli YüksekliÄŸini DeÄŸiÅŸtir" msgid "Change Ray Shape Length" msgstr "Işın Åžeklinin UzunluÄŸunu DeÄŸiÅŸtir" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "EÄŸri Noktası Konumu Ayarla" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "EÄŸri Noktası Konumu Ayarla" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Silindir Yarıçapını DeÄŸiÅŸtir" @@ -14194,6 +14252,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Bir model ayarlanana kadar bu gövde yok sayılır." @@ -15449,9 +15543,6 @@ msgstr "Sabit deÄŸerler deÄŸiÅŸtirilemez." #~ msgid "I see..." #~ msgstr "Anlıyorum..." -#~ msgid "Can't open '%s'." -#~ msgstr "'%s' açılamıyor." - #~ msgid "Ugh" #~ msgstr "Öff" diff --git a/editor/translations/tt.po b/editor/translations/tt.po index 4ff5555363..e7b37069b7 100644 --- a/editor/translations/tt.po +++ b/editor/translations/tt.po @@ -338,6 +338,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -362,10 +363,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -920,7 +936,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2211,6 +2227,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2990,10 +3017,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3598,6 +3621,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8275,6 +8306,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8299,6 +8336,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11706,6 +11749,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13300,6 +13351,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/tzm.po b/editor/translations/tzm.po index 7d41f508ee..8c7d3f272c 100644 --- a/editor/translations/tzm.po +++ b/editor/translations/tzm.po @@ -336,6 +336,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -360,10 +361,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -918,7 +934,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2209,6 +2225,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2988,10 +3015,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3596,6 +3619,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8273,6 +8304,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8297,6 +8334,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11704,6 +11747,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13298,6 +13349,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/uk.po b/editor/translations/uk.po index adccdfd91f..a889e83e19 100644 --- a/editor/translations/uk.po +++ b/editor/translations/uk.po @@ -21,8 +21,8 @@ msgid "" msgstr "" "Project-Id-Version: Ukrainian (Godot Engine)\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-08-02 02:00+0000\n" -"Last-Translator: IllusiveMan196 <hamsterrv@gmail.com>\n" +"PO-Revision-Date: 2021-08-04 12:10+0000\n" +"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" "Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/" "godot/uk/>\n" "Language: uk\n" @@ -356,6 +356,7 @@ msgstr "Змінити режим циклу анімації" msgid "Remove Anim Track" msgstr "Видалити доріжку" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Створити нову доріжку Ð´Ð»Ñ %s Ñ– вÑтавити ключ?" @@ -380,10 +381,28 @@ msgstr "Створити" msgid "Anim Insert" msgstr "Ð’Ñтавити анімацію" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Ðеможливо відкрити '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ÐнімаціÑ" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer не може анімувати Ñебе, лише інших відтворювачів." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ВлаÑтивоÑті «%s» не Ñ–Ñнує." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Створити Ñ– вÑтавити анімацію" @@ -596,9 +615,8 @@ msgid "Go to Previous Step" msgstr "До попереднього кроку" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Скинути" +msgstr "ЗаÑтоÑувати ÑкиданнÑ" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -617,9 +635,8 @@ msgid "Use Bezier Curves" msgstr "ВикориÑтовувати криві Безьє" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Ð’Ñтавити доріжки" +msgstr "Створити доріжки RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -944,7 +961,6 @@ msgid "Edit..." msgstr "Змінити…" #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Перейти до методу" @@ -964,9 +980,9 @@ msgstr "Створити новий %s" msgid "No results for \"%s\"." msgstr "Ðічого не знайдено Ð´Ð»Ñ Â«%s»." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Ðемає опиÑу Ð´Ð»Ñ %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1066,17 +1082,16 @@ msgid "Owners Of:" msgstr "ВлаÑники:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" "Вилучити позначені файли з проєкту? (без можливоÑті ÑкаÑувати)\n" -"Вилучені файли можна буде знайти Ñ– відновити у теці Ñмітника ÑиÑтеми." +"Залежно від конфігурації вашої файлової ÑиÑтеми, вилучені файли буде або " +"переÑунуто до теки Ñмітника, або вилучено оÑтаточно." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1087,7 +1102,8 @@ msgstr "" "Файли, Ñкі ви вилучаєте, потрібні Ð´Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ñ†ÐµÐ·Ð´Ð°Ñ‚Ð½Ð¾Ñті інших " "реÑурÑів.\n" "Вилучити Ñ—Ñ… попри це? (без ÑкаÑуваннÑ)\n" -"Вилучені файли можна знайти Ñ– відновити у теці Ñмітника ÑиÑтеми." +"Залежно від конфігурації вашої файлової ÑиÑтеми, вилучені файли буде або " +"переÑунуто до теки Ñмітника, або вилучено оÑтаточно." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1257,41 +1273,37 @@ msgid "Licenses" msgstr "Ліцензії" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби відкрити файл пакунка (дані не у форматі ZIP)." +msgstr "" +"Помилка під Ñ‡Ð°Ñ Ñпроби відкрити файл пакунка Ð´Ð»Ñ Â«%s» (не у форматі ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (вже Ñ–Ñнує)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "ВміÑÑ‚ пакунка «%s» — конфлікт %d файлів із вашим проєктом:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "ВміÑÑ‚ пакунка «%s» — немає конфліктів файлів із вашим проєктом:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Ð Ð¾Ð·Ð¿Ð°ÐºÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑів" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ такі файли з пакунка:" +msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ з пакунка «%s» такі файли:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "І ще %s файлів." +msgstr "(Ñ– ще %s файлів)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Пакунок уÑпішно вÑтановлено!" +msgstr "Пакунок «%s» уÑпішно вÑтановлено!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1303,9 +1315,8 @@ msgid "Install" msgstr "Ð’Ñтановити" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Ð’Ñтановлювач пакета" +msgstr "Ð’Ñтановлювач пакукнків" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1368,9 +1379,8 @@ msgid "Bypass" msgstr "Обхід" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" -msgstr "Опції шини" +msgstr "Параметри шини" #: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp #: editor/plugins/animation_player_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -1536,13 +1546,12 @@ msgid "Can't add autoload:" msgstr "Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ автозавантаженнÑ:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Файл не Ñ–Ñнує." +msgstr "%s не Ñ” коректним шлÑхом. Файла не Ñ–Ñнує." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s Ñ” некоректним шлÑхом. Його немає у шлÑху реÑурÑів (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1566,9 +1575,8 @@ msgid "Name" msgstr "Ðазва" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Змінна" +msgstr "Загальна змінна" #: editor/editor_data.cpp msgid "Paste Params" @@ -1724,7 +1732,7 @@ msgstr "Редактор Ñкриптів" #: editor/editor_feature_profile.cpp msgid "Asset Library" -msgstr "Бібліотека реÑурÑів" +msgstr "Бібліотека пакунків" #: editor/editor_feature_profile.cpp msgid "Scene Tree Editing" @@ -1744,48 +1752,55 @@ msgstr "Бічна панель імпортуваннÑ" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Ðадає змогу переглÑдати Ñ– редагувати проÑторові Ñцени." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"Ðадає змогу редагувати Ñкрипти за допомогою вбудованого редактора Ñкриптів." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Ðадає вбудований доÑтупу до бібліотеки пакунків." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Ðадає змогу редагувати ієрархію вузлів на бічній панелі Ñцени." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Ðадає змогу працювати із Ñигналами Ñ– групами вузла, Ñкий позначено на бічній " +"панелі Ñцени." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Ðадає змогу здійÑнювати навігацію локальною файловою ÑиÑтемою за допомогою " +"відповідної бічної панелі." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Ðадає змогу налаштувати параметри Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¾ÐºÑ€ÐµÐ¼Ð¸Ñ… пакунків. " +"Потребує Ð´Ð»Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ бічної панелі FileSystem." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Поточний)" +msgstr "(поточний)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(немає)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"Вилучити поточний позначений профіль, «%s»? Дію не може бути ÑкаÑовано." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1816,19 +1831,16 @@ msgid "Enable Contextual Editor" msgstr "Увімкнути контекÑтуальний редактор" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "ВлаÑтивоÑті:" +msgstr "ВлаÑтивоÑті клаÑу:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "МожливоÑті" +msgstr "ОÑновні можливоÑті:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Увімкнені клаÑи:" +msgstr "Вузли Ñ– клаÑи:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1847,23 +1859,20 @@ msgid "Error saving profile to path: '%s'." msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби зберегти профіль до каталогу: «%s»." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Відновити типові параметри" +msgstr "ПовернутиÑÑ Ð´Ð¾ типового" #: editor/editor_feature_profile.cpp msgid "Current Profile:" msgstr "Поточний профіль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Витерти профіль" +msgstr "Створити профіль" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Вилучити плитку" +msgstr "Вилучити профіль" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1883,18 +1892,18 @@ msgid "Export" msgstr "ЕкÑпортуваннÑ" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Поточний профіль:" +msgstr "Ðалаштувати позначений профіль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Параметри клаÑу:" +msgstr "Додаткові параметри:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Створіть або імпортуйте профіль Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупних клаÑів Ñ– " +"влаÑтивоÑтей." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1921,9 +1930,8 @@ msgid "Select Current Folder" msgstr "Вибрати поточну теку" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Файл Ñ–Ñнує, перезапиÑати його?" +msgstr "Файл вже Ñ–Ñнує. ПерезапиÑати?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2316,6 +2324,17 @@ msgid "New Window" msgstr "Ðове вікно" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ОбертаєтьÑÑ, коли перемальовуєтьÑÑ Ð²Ñ–ÐºÐ½Ð¾ редактора." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Ðеможливо зберегти імпортовані реÑурÑи." @@ -2545,13 +2564,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"У поточної Ñцени немає кореневого вузла, але %d змінених зовнішніх реÑурÑів " +"було збережено попри це." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб можна було зберегти Ñцену, потрібен кореневий вузол." +msgstr "" +"Ð”Ð»Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñцени потрібен кореневий вузол. Ви можете додати кореневий " +"вузол за допомогою бічної панелі ієрархії Ñцени." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2937,9 +2959,8 @@ msgid "Orphan Resource Explorer..." msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ñиротілими реÑурÑами…" #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Перейменувати проєкт" +msgstr "Перезавантажити поточний проєкт" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3100,13 +3121,12 @@ msgid "Help" msgstr "Довідка" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Відкрити документацію" +msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð² інтернеті" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Ð—Ð°Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ– відповіді" #: editor/editor_node.cpp msgid "Report a Bug" @@ -3114,7 +3134,7 @@ msgstr "Повідомити про ваду" #: editor/editor_node.cpp msgid "Suggest a Feature" -msgstr "" +msgstr "Запропонувати можливіÑть" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3125,9 +3145,8 @@ msgid "Community" msgstr "Спільнота" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Про" +msgstr "Про Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3179,10 +3198,6 @@ msgid "Save & Restart" msgstr "Зберегти Ñ– перезапуÑтити" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ОбертаєтьÑÑ, коли перемальовуєтьÑÑ Ð²Ñ–ÐºÐ½Ð¾ редактора." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Оновлювати неперервно" @@ -3225,14 +3240,12 @@ msgid "Manage Templates" msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Ð’Ñтановити з файлу" +msgstr "Ð’Ñтановити з файла" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Виберіть джерело Ñітки:" +msgstr "Виберіть файл початкових кодів android" #: editor/editor_node.cpp msgid "" @@ -3314,9 +3327,8 @@ msgid "Select" msgstr "Виділити" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Вибрати поточну теку" +msgstr "Вибрати поточний" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3351,9 +3363,8 @@ msgid "No sub-resources found." msgstr "Підлеглих реÑурÑів не знайдено." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Підлеглих реÑурÑів не знайдено." +msgstr "Відкрити ÑпиÑок підлеглих реÑурÑів." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3380,14 +3391,12 @@ msgid "Update" msgstr "Оновити" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "ВерÑÑ–Ñ:" +msgstr "ВерÑÑ–Ñ" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Ðвтори" +msgstr "Ðвтор" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3400,14 +3409,12 @@ msgid "Measure:" msgstr "Вимірювати:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Ð§Ð°Ñ ÐºÐ°Ð´Ñ€Ñƒ (Ñек)" +msgstr "Ð§Ð°Ñ ÐºÐ°Ð´Ñ€Ñƒ (мÑ)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Середній Ñ‡Ð°Ñ (Ñек)" +msgstr "Середній Ñ‡Ð°Ñ (мÑ)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3434,6 +3441,12 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Включно: включає Ñ‡Ð°Ñ Ð· інших функцій, Ñкі викликає Ñ†Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ.\n" +"СкориÑтайтеÑÑ Ñ†Ð¸Ð¼ варіантом Ð´Ð»Ñ Ð²Ð¸ÑÐ²Ð»ÐµÐ½Ð½Ñ Ð²ÑƒÐ·ÑŒÐºÐ¸Ñ… міÑць.\n" +"\n" +"Цей об'єкт: визначати витрачений Ñ‡Ð°Ñ Ñƒ Ñамій функції, не в інших функціÑÑ…, " +"Ñкі Ñ†Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ°Ñ”.\n" +"СкориÑтайтеÑÑ Ñ†Ð¸Ð¼ варіантом Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ окремих функцій Ð´Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ñ–Ð·Ð°Ñ†Ñ–Ñ—." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3556,9 +3569,8 @@ msgid "Paste" msgstr "Ð’Ñтавити" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" -msgstr "Перетворити на %s" +msgstr "Перетворити до %s" #: editor/editor_resource_picker.cpp editor/property_editor.cpp msgid "New %s" @@ -3608,10 +3620,9 @@ msgid "Did you forget the '_run' method?" msgstr "Ви забули метод '_run'?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Утримуйте натиÑнутою Ctrl, щоб заокруглити до цілих. Утримуйте натиÑнутою " +"Утримуйте натиÑнутою %s, щоб заокруглити до цілих. Утримуйте натиÑнутою " "Shift, щоб зміни були точнішими." #: editor/editor_sub_scene.cpp @@ -3632,49 +3643,43 @@ msgstr "Імпортувати з вузла:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Відкрити теку, Ñка міÑтить ці шаблони." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Вилучити ці шаблони." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "Ðемає файла «%s»." +msgstr "Ðемає доÑтупних дзеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð·ÐµÑ€ÐºÐ°Ð», будь лаÑка, зачекайте..." +msgstr "Отримуємо ÑпиÑок дзеркал…" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Розпочинаємо Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…â€¦" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Помилка під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ за такою адреÑою:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ дзеркала..." +msgstr "Ð’Ñтановлюємо з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· дзеркалом…" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Ðеможливо розпізнати ім'Ñ Ñ…Ð¾Ñта:" +msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ вузол за бажаною адреÑою." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Ðе вдалоÑÑ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ÑÑ Ð´Ð¾ хоÑту:" +msgstr "Ðе вдалоÑÑ Ð²Ñтановити з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· дзеркалом." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Ðемає відповіді від хоÑта:" +msgstr "Ðемає відповіді від дзеркала." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3682,18 +3687,16 @@ msgid "Request failed." msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Запит не вдавÑÑ, забагато перенаправлень" +msgstr "Спроба виконати запит завершилаÑÑ Ñ†Ð¸ÐºÐ»Ñ–Ñ‡Ð½Ð¸Ð¼ переÑпрÑмовуваннÑм." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит." +msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… завершено; видобуваємо шаблони…" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3712,15 +3715,14 @@ msgid "Error getting the list of mirrors." msgstr "Помилка під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑпиÑку дзеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" -"Помилка під Ñ‡Ð°Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ JSON ÑпиÑку дзеркал. Будь лаÑка, повідомте про цю " -"ваду!" +"Помилка під Ñ‡Ð°Ñ Ñпроби обробити JSON зі ÑпиÑком дзеркал. Будь лаÑка, " +"повідомте розробникам про цю помилку!" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Ðайкраще доÑтупне дзеркало" #: editor/export_template_manager.cpp msgid "" @@ -3773,24 +3775,21 @@ msgid "SSL Handshake Error" msgstr "Помилка SSL РукоÑтиÑканнÑ" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Ðеможливо відкрити ZIP-файл шаблону екÑпорту." +msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл шаблонів екÑпортуваннÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Ðеправильний формат version.txt у шаблонах: %s." +msgstr "" +"Ðекоректне Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ version.txt у файлі шаблонів екÑпортуваннÑ: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Файл version.txt не знайдено у шаблонах." +msgstr "У файлі шаблонів екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ¼Ð°Ñ” version.txt." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ»Ñху Ð´Ð»Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²:" +msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ»Ñху Ð´Ð»Ñ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸Ñ… шаблонів:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3801,9 +3800,8 @@ msgid "Importing:" msgstr "ІмпортуваннÑ:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Вилучити верÑÑ–ÑŽ шаблону '%s'?" +msgstr "Вилучити шаблони Ð´Ð»Ñ Ð²ÐµÑ€ÑÑ–Ñ— «%s»?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3820,57 +3818,65 @@ msgstr "Поточна верÑÑ–Ñ:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Ðе виÑтачає шаблонів екÑпортуваннÑ. Отримайте Ñ—Ñ… або вÑтановіть з файла." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Шаблони екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñтановлено Ñ– приготовано до викориÑтаннÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Відкрити файл" +msgstr "Відкрити теку" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." -msgstr "" +msgstr "Відкрити теку, що міÑтить вÑтановлені шаблони Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— верÑÑ–Ñ—." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Видалити" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Початкове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð»Ñ–Ñ‡Ð¸Ð»ÑŒÐ½Ð¸ÐºÐ°" +msgstr "Вилучити шаблони Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— верÑÑ–Ñ—." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Помилка завантаженнÑ" +msgstr "Джерело отриманнÑ:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ЗапуÑтити в браузері" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Помилка копіюваннÑ" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Отримати Ñ– вÑтановити" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Отримати Ñ– вÑтановити шаблони Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— верÑÑ–Ñ— із найкращого можливого " +"дзеркала." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "Ð”Ð»Ñ Ñ‚ÐµÑтових збірок не передбачено офіційних шаблонів екÑпортуваннÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Ð’Ñтановити з файлу" +msgstr "Ð’Ñтановити з файла" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Імпортувати шаблони з ZIP-файлу" +msgstr "Ð’Ñтановити шаблони з локального файла." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3878,19 +3884,16 @@ msgid "Cancel" msgstr "СкаÑувати" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Ðеможливо відкрити ZIP-файл шаблону екÑпорту." +msgstr "СкаÑувати Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Ð’Ñтановлені верÑÑ–Ñ—:" +msgstr "Інші вÑтановлені верÑÑ–Ñ—:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Видалити" +msgstr "Вилучити шаблон" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3905,6 +3908,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð² буде продовжено.\n" +"Під Ñ‡Ð°Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ðµ тимчаÑове «замерзаннÑ» редактора." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4052,35 +4057,32 @@ msgid "Collapse All" msgstr "Згорнути вÑе" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Шукати файли" +msgstr "УпорÑдкувати файли" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "УпорÑдкувати за назвою (зроÑтаннÑ)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "УпорÑдкувати за назвою (ÑпаданнÑ)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "УпорÑдкувати за типом (зроÑтаннÑ)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "УпорÑдкувати за типом (ÑпаданнÑ)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "ВоÑтаннє змінено" +msgstr "УпорÑдкувати за оÑтаннім внеÑеннÑм змін" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "ВоÑтаннє змінено" +msgstr "УпорÑдкувати за початковим внеÑеннÑм змін" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4092,7 +4094,7 @@ msgstr "Перейменувати..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "ФокуÑувати поле Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4400,14 +4402,12 @@ msgid "Failed to load resource." msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ реÑурÑ." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "ВлаÑтивоÑті" +msgstr "Копіювати влаÑтивоÑті" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "ВлаÑтивоÑті" +msgstr "Ð’Ñтавити влаÑтивоÑті" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4432,23 +4432,20 @@ msgid "Save As..." msgstr "Зберегти Ñк..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Ðе в реÑурÑному шлÑху." +msgstr "Додаткові параметри реÑурÑу." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Редагувати буфер реÑурÑів" +msgstr "Редагувати реÑÑƒÑ€Ñ Ð· буфера обміну" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Копіювати реÑурÑ" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Зробити вбудованим" +msgstr "Зробити реÑÑƒÑ€Ñ Ð²Ð±ÑƒÐ´Ð¾Ð²Ð°Ð½Ð¸Ð¼" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4463,9 +4460,8 @@ msgid "History of recently edited objects." msgstr "ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð½ÐµÑ‰Ð¾Ð´Ð°Ð²Ð½Ð¾ відредагованих об'єктів." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Відкрити документацію" +msgstr "Відкрити документацію Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ об'єкта." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4476,9 +4472,8 @@ msgid "Filter properties" msgstr "Фільтрувати влаÑтивоÑті" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "ВлаÑтивоÑті об'єкта." +msgstr "Керувати влаÑтивоÑÑ‚Ñми об'єкта." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4726,9 +4721,8 @@ msgid "Blend:" msgstr "Змішувати:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Змінено параметр" +msgstr "Змінено параметр:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5460,11 +5454,11 @@ msgstr "Ð’Ñе" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Шукати шаблони, проєкти та демонÑтрації" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Пошук пакунків (із виключеннÑм шаблонів, проєктів та демонÑтрацій)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5508,7 +5502,7 @@ msgstr "ZIP файл реÑурÑів" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "ПуÑк/Пауза проÑÐ»ÑƒÑ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5769,13 +5763,12 @@ msgstr "Змінити прив'Ñзки" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"ÐŸÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸ гри\n" -"Замінює камеру гри камерою видимої облаÑті редактора." +"ÐŸÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸ проєкту\n" +"Замінює поточну камеру проєкту камерою видимої облаÑті редактора." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5784,6 +5777,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"ÐŸÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸ проєкту\n" +"Ðемає запущеного екземплÑра проєкту. ЗапуÑтіть проєкт з вікна редактора, щоб " +"ÑкориÑтатиÑÑ Ñ†Ñ–Ñ”ÑŽ можливіÑтю." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5851,31 +5847,27 @@ msgstr "Режим виділеннÑ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Вилучити позначений вузол або перехід." +msgstr "ПеретÑгуваннÑ: обертати позначений вузол навколо опорної точки." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+ПеретÑгнути: переміÑтити" +msgstr "Alt+ПеретÑгнути: переміÑтити позначений вузол." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Вилучити позначений вузол або перехід." +msgstr "V: вÑтановити позицію опорної точки позначеного вузла." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Показати ÑпиÑок уÑÑ–Ñ… об'єктів, натиÑнутих на позицію\n" -"(так Ñамо, Ñк Ðльт+ПКМ у режимі вибору)." +"Alt+ПКМ: показати ÑпиÑок уÑÑ–Ñ… вузлів у позиції клацаннÑ, включно із " +"заблокованими." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "ПКМ: додати вузол у позиції клацаннÑ." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6113,14 +6105,12 @@ msgid "Clear Pose" msgstr "ОчиÑтити позу" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Додати вузол" +msgstr "Додати вузол тут" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Сцени екземплÑра" +msgstr "ЕкземплÑÑ€ Ñцени тут" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6136,49 +6126,43 @@ msgstr "ÐŸÐ°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "МаÑштаб у 3,125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "МаÑштаб у 6,25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "МаÑштаб у 12,5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "МаÑштаб у 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6424,9 +6408,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Ðе вдалоÑÑ Ñтворити єдину опуклу форму зіткненнÑ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Створити єдину опуклу форму" +msgstr "Створити Ñпрощену опуклу форму" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6461,9 +6444,8 @@ msgid "No mesh to debug." msgstr "Ðемає Ñітки Ð´Ð»Ñ Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "Модель не має UV на цьому шарі" +msgstr "Сітка не має UV у шарі %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6528,9 +6510,8 @@ msgstr "" "Цей найшвидший (але найменш точний) варіант Ð´Ð»Ñ Ð²Ð¸ÑÐ²Ð»ÐµÐ½Ð½Ñ Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½ÑŒ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Створити єдину опуклу облаÑть зіткненнÑ" +msgstr "Створити Ñпрощену опуклу облаÑть зіткненнÑ" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6538,20 +6519,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Створює Ñпрощену опуклу форму зіткненнÑ.\n" +"Це Ñхоже на єдину форму зіткненнÑ, але може призвеÑти у деÑких випадках до " +"проÑтішої геометрії ціною точноÑті." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Створити декілька опуклих облаÑтей зіткненнÑ" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "Створює заÑновану на багатокутниках форму зіткненнÑ.\n" -"Цей проміжний за швидкіÑтю варіант між наведеними вище двома варіантами." +"Цей проміжний за швидкодією варіант між єдиною опуклою формою Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½Ð½Ñ Ñ– " +"заÑнованою на багатокутниках формою зіткненнÑ." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7195,24 +7179,20 @@ msgid "ResourcePreloader" msgstr "Передзавантажувач реÑурÑів" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Віддзеркалити горизонтально" +msgstr "Віддзеркалити портали" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "КількіÑть генерованих точок:" +msgstr "Створити точки кімнати" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "КількіÑть генерованих точок:" +msgstr "Створити точки" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Віддзеркалити горизонтально" +msgstr "Віддзеркалити портал" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7778,20 +7758,17 @@ msgid "None" msgstr "Ðемає" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Режим повороту" +msgstr "Обертати" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "ПеренеÑеннÑ:" +msgstr "ПереÑунути" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "МаÑштаб:" +msgstr "МаÑштаб" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7814,52 +7791,44 @@ msgid "Animation Key Inserted." msgstr "Ð’Ñтавлено ключ анімації." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "ХилитаннÑ" +msgstr "Тон:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "РиÑканнÑ:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Розмір: " +msgstr "Розмір:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Ðамальовано об'єктів" +msgstr "Ðамальовано об'єктів:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Зміни матеріалу" +msgstr "Зміни матеріалу:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Зміни шейдерів" +msgstr "Зміни шейдерів:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Зміни поверхонь" +msgstr "Зміни поверхонь:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Виклики заÑобу малюваннÑ" +msgstr "Ðамалювати виклики:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Вершини" +msgstr "Вершини:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "ЧаÑтота кадрів: %d (%s мÑ)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8014,9 +7983,8 @@ msgid "Freelook Slow Modifier" msgstr "Модифікатор швидкоÑті довільного оглÑду" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Змінити розмір камери" +msgstr "Перемкнути попередній переглÑд камери" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8039,9 +8007,8 @@ msgstr "" "грі." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Перетворити на %s" +msgstr "Перетворити кімнати" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8062,7 +8029,6 @@ msgstr "" "Ðапівзакрите око: Gizmo Ñ” також видимим крізь непрозорі поверхні («рентген»)." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Приліпити вузли до підлоги" @@ -8080,7 +8046,7 @@ msgstr "За допомогою функції прив'Ñзки" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Перетворює кімнати Ð´Ð»Ñ Ð²Ñ–Ð´Ð±Ñ€Ð°ÐºÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ñ€Ñ‚Ð°Ð»Ñƒ." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8176,9 +8142,8 @@ msgid "View Grid" msgstr "ПереглÑд ґратки" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Параметри панелі переглÑду" +msgstr "ПереглÑнути Ð²Ñ–Ð´Ð±Ñ€Ð°ÐºÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Portal" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8500,221 +8465,196 @@ msgid "TextureRegion" msgstr "TextureRegion" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Колір" +msgstr "Кольори" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Шрифт" +msgstr "Шрифти" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Піктограма" +msgstr "Піктограми" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Style Box" +msgstr "Стильові панелі" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} кольорів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Підлеглих реÑурÑів не знайдено." +msgstr "Кольорів не знайдено." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "КонÑтанти" +msgstr "{num} Ñталих" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Сталий колір." +msgstr "Сталих не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} шрифтів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Ðе знайдено!" +msgstr "Шрифтів не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} піктограм" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Ðе знайдено!" +msgstr "Піктограм не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} панелей Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Підлеглих реÑурÑів не знайдено." +msgstr "Панелей Ñтилів не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} зараз позначених" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Ðічого не позначено Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Імпортувати тему" +msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів теми" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів, {n} з {n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Вийти з редактора?" +msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€Ð°" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Ðналіз" +msgstr "ЗакріпленнÑ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Фільтри:" +msgstr "Фільтр:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "З даними" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Виберіть вузол" +msgstr "Вибір за типом даних:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Виберіть поділ Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ витираннÑ." +msgstr "Вибрати уÑÑ– видимі запиÑи кольорів." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи кольорів та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів кольорів." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи Ñталих." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– запиÑи уÑÑ–Ñ… видимих Ñталих та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів Ñталих." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи шрифтів." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи шрифтів та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів шрифтів." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи піктограм." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи піктограм та їхні дані." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів піктограм." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи панелей Ñтилів." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи панелей Ñтилів та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів панелей Ñтилів." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"ПопередженнÑ: Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… піктограм може значно збільшити розмір вашого " +"реÑурÑу теми." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Згорнути вÑе" +msgstr "Згорнути типи." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Розгорнути вÑе" +msgstr "Розгорнути типи." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Виберіть файл шаблону" +msgstr "Вибрати уÑÑ– запиÑи тем." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Виберіть пункти" +msgstr "Вибрати з даними" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Вибрати уÑÑ– запиÑи тем із даними запиÑу." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Виділити вÑе" +msgstr "ЗнÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· уÑÑ–Ñ…" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… запиÑів тем." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Імпортувати Ñцену" +msgstr "Імпортувати позначене" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8722,271 +8662,247 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"Ðа панелі Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів позначено деÑкі запиÑи. Якщо закрити це " +"вікно, Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ знÑто.\n" +"Закрити вікно попри це?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи кольорів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Вилучити елемент" +msgstr "Перейменувати запиÑ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи Ñталих" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи шрифтів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи піктограм" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи панелей Ñтилів" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Додати елементи клаÑу" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Додати елементи клаÑу" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ñталої" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Додати елемент" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Додати елемент" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ–ÐºÑ‚Ð¾Ð³Ñ€Ð°Ð¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Додати уÑÑ– елементи" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ñ– Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Вилучити елементи клаÑу" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Вилучити елементи клаÑу" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ Ñталої" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Перейменувати вузол" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Перейменувати вузол" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ–ÐºÑ‚Ð¾Ð³Ñ€Ð°Ð¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Вилучити вибраний елемент" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ñ– Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "ÐеприпуÑтимий файл, це не ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾-шини." +msgstr "Ðекоректний файл. Файл не Ñ” реÑурÑом теми." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "Ðекоректний файл. Файл збігаєтьÑÑ Ñ–Ð· редагованим реÑурÑом теми." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸" +msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñами теми" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Редагований елемент" +msgstr "Редагувати запиÑи" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Тип:" +msgstr "Типи:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Тип:" +msgstr "Додати тип:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Додати елемент" +msgstr "Додати запиÑ:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Додати уÑÑ– елементи" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ñ– Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Вилучити елемент" +msgstr "Вилучити запиÑи:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "Вилучити елементи клаÑу" +msgstr "Вилучити запиÑи клаÑу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Вилучити елементи клаÑу" +msgstr "Вилучити нетипові запиÑи" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "Вилучити уÑÑ– елементи" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Тема елементів ГІК" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ñ‚ÐµÐ¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Ім'Ñ Ð’ÑƒÐ·Ð»Ð°:" +msgstr "Стара назва:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Імпортувати тему" +msgstr "Імпортовані пункти" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Типовий" +msgstr "Типова тема" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Редагувати тему" +msgstr "Тема редактора" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Вилучити реÑурÑ" +msgstr "Виберіть реÑÑƒÑ€Ñ Ñ–Ð½ÑˆÐ¾Ñ— теми:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Імпортувати тему" +msgstr "Інша тема" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Перейменувати доріжку" +msgstr "Підтвердити Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Пакетне перейменуваннÑ" +msgstr "СкаÑувати Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "ПеревизначеннÑ" +msgstr "Перевизначити запиÑ" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Відшпилити цю панель Ñтилів Ñк головний Ñтиль." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Пришпилити цю панель Ñтилів Ñк оÑновний Ñтиль. Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ влаÑтивоÑтей " +"призведе до Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð¸Ñ… Ñами влаÑтивоÑтей в уÑÑ–Ñ… інших панелÑÑ… Ñтилів " +"цього типу." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Тип" +msgstr "Додати тип" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Додати елемент" +msgstr "Додати тип запиÑу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Тип вузлів" +msgstr "Типи вузлів:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Завантажити типовий" +msgstr "Показати типовий" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Показати запиÑи Ñтандартних типів разом із запиÑами, Ñкі було перевизначено." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "ПеревизначеннÑ" +msgstr "Перевизначити уÑе" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Перевизначити уÑÑ– запиÑи Ñтандартних типів." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Тема" +msgstr "Тема:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸ екÑпортуваннÑ…" +msgstr "Керувати запиÑами…" #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Додати, вилучити, упорÑдкувати або імпортувати запиÑи тем." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Попередній переглÑд" +msgstr "Додати Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Оновити переглÑд" +msgstr "Типове Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Виберіть джерело Ñітки:" +msgstr "Виберіть Ñцену графічного інтерфейÑу:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Перемкнути заÑіб вибору керуваннÑ, Ñкий уможливлює візуальний вибір типів " +"ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9021,9 +8937,8 @@ msgid "Checked Radio Item" msgstr "Позначений пункт варіанта" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Імен. розд." +msgstr "Іменований роздільник" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9076,19 +8991,21 @@ msgstr "Має,Багато,Параметрів" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Ðекоректний шлÑÑ…. Ймовірно, реÑÑƒÑ€Ñ PackedScene було переÑунуто або вилучено." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"Ðекоректний реÑÑƒÑ€Ñ PackedScene. Кореневим вузлом реÑурÑу має бути вузол " +"Control." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "ÐеприпуÑтимий файл, це не ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾-шини." +msgstr "Ðекоректний файл. Файл не Ñ” реÑурÑом PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Перезавантажити Ñцену Ð´Ð»Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ—Ñ— найактуальнішого Ñтану." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10493,9 +10410,8 @@ msgid "VisualShader" msgstr "VisualShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Змінити візуальну влаÑтивіÑть" +msgstr "Змінити візуальну влаÑтивіÑть:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10621,9 +10537,8 @@ msgid "Script" msgstr "Скрипт" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Режим екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñкрипту:" +msgstr "Режим екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ GDScript:" #: editor/project_export.cpp msgid "Text" @@ -10631,21 +10546,20 @@ msgstr "ТекÑÑ‚" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Зібраний байткод (швидше завантаженнÑ)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Зашифровано (ключ можна вказати нижче)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Ðекоректний ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ (ключ має ÑкладатиÑÑ Ñ–Ð· 64 Ñимволів)" +msgstr "" +"Ðекоректний ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ (ключ має ÑкладатиÑÑ Ñ–Ð· 64 шіÑтнадцÑткових цифр)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñкрипту (256-бітове шіÑтнадцÑткове чиÑло):" +msgstr "Ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ GDScript (256-бітове шіÑтнадцÑткове чиÑло):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10717,7 +10631,6 @@ msgid "Imported Project" msgstr "Імпортований проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Ðекоректна назва проєкту." @@ -10945,14 +10858,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Ви Ñправді хочете запуÑтити %d проєктів одночаÑно?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Вибрати приÑтрій зі ÑпиÑку" +msgstr "Вилучити зі ÑпиÑку %d проєктів?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Вибрати приÑтрій зі ÑпиÑку" +msgstr "Вилучити цей проєкт зі ÑпиÑку?" #: editor/project_manager.cpp msgid "" @@ -10985,9 +10896,8 @@ msgid "Project Manager" msgstr "Керівник проєкту" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Проєкти" +msgstr "Локальні проєкти" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -10998,23 +10908,20 @@ msgid "Last Modified" msgstr "ВоÑтаннє змінено" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "ЕкÑпортувати проєкт" +msgstr "Редагувати проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Перейменувати проєкт" +msgstr "ЗапуÑтити проєкт" #: editor/project_manager.cpp msgid "Scan" msgstr "Сканувати" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Проєкти" +msgstr "Сканувати проєкти" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11025,14 +10932,12 @@ msgid "New Project" msgstr "Ðовий проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Імпортований проєкт" +msgstr "Імпортувати проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Перейменувати проєкт" +msgstr "Вилучити проєкт" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11040,12 +10945,11 @@ msgstr "Вилучити пропущене" #: editor/project_manager.cpp msgid "About" -msgstr "Про" +msgstr "ВідомоÑті" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Бібліотека реÑурÑів" +msgstr "Проєкти бібліотеки пакунків" #: editor/project_manager.cpp msgid "Restart Now" @@ -11057,7 +10961,7 @@ msgstr "Вилучити уÑÑ–" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Також вилучити вміÑÑ‚ проєкту (без можливоÑті ÑкаÑуваннÑ!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11072,20 +10976,18 @@ msgstr "" "Бажаєте переглÑнути офіційні приклади проєктів з бібліотеки реÑурÑів?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Фільтрувати влаÑтивоÑті" +msgstr "Фільтр проєктів" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"Поле пошуку фільтрує проєкти за назвою Ñ– оÑтаннім компонентом шлÑху.\n" +"Це поле фільтрує проєкти за назвою Ñ– оÑтаннім компонентом шлÑху.\n" "Щоб виконати Ñ„Ñ–Ð»ÑŒÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñ–Ð² за назвою Ñ– повним шлÑхом, у запиті має " -"бути принаймні один Ñимвол `/`." +"бути принаймні один Ñимвол «/»." #: editor/project_settings_editor.cpp msgid "Key " @@ -11093,7 +10995,7 @@ msgstr "Клавіша " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Фізична клавіша" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11141,7 +11043,7 @@ msgstr "ПриÑтрій" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (фізичний)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11284,23 +11186,21 @@ msgid "Override for Feature" msgstr "Перевизначено Ð´Ð»Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾Ñті" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Додати переклад" +msgstr "Додати %d перекладів" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Вилучити переклад" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу додає переÑпрÑмуваннÑ" +msgstr "Повторна прив'Ñзка реÑурÑів перекладу: Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %d шлÑхів" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу додає переÑпрÑмуваннÑ" +msgstr "" +"Повторна прив'Ñзка реÑурÑів перекладу: Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %d повторних прив'Ñзок" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11746,12 +11646,15 @@ msgstr "Вилучити вузол «%s»?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Щоб можна було зберегти гілку Ñк Ñцену, Ñцену має бути відкрито у редакторі." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Щоб можна було зберегти гілку Ñк Ñцену, має бути позначено лише один вузол, " +"а у Ð²Ð°Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾ %d." #: editor/scene_tree_dock.cpp msgid "" @@ -11760,6 +11663,11 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ гілку кореневого вузла Ñк екземплÑÑ€ Ñцени.\n" +"Щоб Ñтворити редаговану копію поточної Ñцени, здублюйте Ñ—Ñ— за допомогою " +"контекÑтного меню бічної панелі файлової ÑиÑтеми\n" +"або Ñтворіть уÑпадковану Ñцену за допомогою пункту меню «Сцена > Створити " +"уÑпадковану Ñцену...»." #: editor/scene_tree_dock.cpp msgid "" @@ -11767,6 +11675,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ гілку вже Ñтвореного екземплÑра Ñцени.\n" +"Щоб Ñтворити варіацію Ñцени, ви можете Ñтворити уÑпадковану Ñцену на оÑнові " +"екземплÑра Ñцени за допомогою пункту меню «Сцена > Створити уÑпадковану " +"Ñцену...»." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12174,6 +12086,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"ПопередженнÑ: викориÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñкрипту назви, Ñка збігаєтьÑÑ Ñ–Ð· назвою " +"вбудованого типу, зазвичай, Ñ” небажаним." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12245,7 +12159,7 @@ msgstr "Помилка копіюваннÑ" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Відкрити початковий код C++ на GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12423,6 +12337,16 @@ msgstr "Змінити виÑоту форми циліндра" msgid "Change Ray Shape Length" msgstr "Змінити довжину форми променÑ" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Задати Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð¾Ñ‡ÐºÐ¸ кривої" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Задати Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð¾Ñ‡ÐºÐ¸ кривої" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Змінити Ñ€Ð°Ð´Ñ–ÑƒÑ Ñ†Ð¸Ð»Ñ–Ð½Ð´Ñ€Ð°" @@ -12534,14 +12458,12 @@ msgid "Object can't provide a length." msgstr "Об'єкт не може надавати довжину." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "ЕкÑпортувати бібліотеку Ñіті" +msgstr "ЕкÑпортувати GLTF2 Ñітки" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "ЕкÑпортувати…" +msgstr "ЕкÑпортувати GLTF…" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12584,9 +12506,8 @@ msgid "GridMap Paint" msgstr "Малюнок GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Вибір Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ GridMap" +msgstr "Вибір GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12838,14 +12759,12 @@ msgid "Add Output Port" msgstr "Додати вихідний порт" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Змінити тип" +msgstr "Змірити тип порту" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Змінити назву вхідного порту" +msgstr "Змінити назву порту" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -12960,9 +12879,8 @@ msgid "Add Preload Node" msgstr "Додати попередньо завантажений вузол" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Додати вузол" +msgstr "Додати вузли" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13227,37 +13145,31 @@ msgstr "Вибрати приÑтрій зі ÑпиÑку" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "Запущено на %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÑього" +msgstr "ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ APK…" #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Видалити" +msgstr "ВилученнÑ…" #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "ЗавантаженнÑ. Будь лаÑка, зачекайте..." +msgstr "Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° приÑтрій. Будь лаÑка, зачекайте..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити підпроцеÑ!" +msgstr "Ðе вдалоÑÑ Ð²Ñтановити на приÑтрій: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "ЗапуÑк кориÑтувацького Ñкрипту..." +msgstr "ЗапуÑк на приÑтрої…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Ðеможливо Ñтворити теку." +msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ на приÑтрої." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13388,40 +13300,38 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ «apksigner».\n" +"Будь лаÑка, перевірте, чи Ñ” програма доÑтупною у каталозі build-tools набору " +"заÑобів Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð¾Ð±ÐºÐ¸ Android.\n" +"Отриманий у результаті %s не підпиÑано." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "ПідпиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ñ–Ð°Ð³Ð½Ð¾Ñтики %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð²,\n" -"будь лаÑка, зачекайте..." +msgstr "ПідпиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¿ÑƒÑку %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ шаблон Ð´Ð»Ñ ÐµÐºÑпорту:" +msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ñховище ключів. Ðеможливо виконати екÑпортуваннÑ." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "«apksigner» повернуто Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку із номером %d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %s..." +msgstr "ПеревірÑємо %s…" #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "%s не пройдено перевірку за допомогою «apksigner»." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÑього" +msgstr "ЕкÑпорт на Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13440,7 +13350,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Ðепідтримуваний формат екÑпортуваннÑ!\n" #: platform/android/export/export.cpp msgid "" @@ -13468,16 +13378,15 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñати файли res://android/build/res/*.xml із назвою проєкту" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ project.godot у каталозі проекту." +msgstr "Ðе вдалоÑÑ ÐµÐºÑпортувати файли проєкту до проєкту gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" +msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл пакунка розширеннÑ!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13506,21 +13415,20 @@ msgstr "" "дані можна знайти у каталозі проєкту gradle." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "Ðе знайдено анімації: «%s»" +msgstr "Пакунок не знайдено: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÑƒÑ€Ñ–Ð²..." +msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ APK…" #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ шаблон Ð´Ð»Ñ ÐµÐºÑпорту:" +msgstr "" +"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ шаблон APK Ð´Ð»Ñ ÐµÐºÑпортуваннÑ:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13529,16 +13437,17 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Ðе виÑтачає бібліотек у шаблоні екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²Ð¸Ð±Ñ€Ð°Ð½Ð¸Ñ… архітектур: %s.\n" +"Будь лаÑка, Ñтворіть шаблон з уÑіма необхідними бібліотеками або зніміть " +"позначку з архітектур із пропущеними бібліотеками у Ñтилі екÑпортуваннÑ." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %s..." +msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð²â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" +msgstr "Ðе вдалоÑÑ ÐµÐºÑпортувати файли проєкту" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13546,7 +13455,7 @@ msgstr "Вирівнюємо APK..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð¿Ð°ÐºÑƒÐ²Ð°Ñ‚Ð¸ тимчаÑовий невирівнÑний APK." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13594,45 +13503,40 @@ msgid "Could not write file:" msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" +msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ Ñпеціальну оболонку HTML:" +msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ оболонку HTML:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Ðеможливо Ñтворити теку." +msgstr "Ðе вдалоÑÑ Ñтворити каталог на Ñервері HTTP:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñцени." +msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби запуÑку Ñервера HTTP:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Ðекоректний ідентифікатор:" +msgstr "Ðекоректний ідентифікатор пакунка:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "ЗаÑвідченнÑ: потрібен код підпиÑуваннÑ." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "ЗаÑвідченнÑ: потрібне Ñтійке Ñередовище запуÑку." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "ЗаÑвідченнÑ: не вказано назву ідентифікатора Apple." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "ЗаÑвідченнÑ: не вказано пароль до ідентифікатора Apple." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14074,6 +13978,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"ВлаÑтивіÑть GIProbe Compress було визначено заÑтарілою через відомі помилки. " +"Вона більше ні на що не впливає.\n" +"Щоб уÑунути це попередженнÑ, вимкніть влаÑтивіÑть Compress у GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14163,14 +14070,20 @@ msgstr "Вузол A Ñ– вузол B має бути різними PhysicsBody" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." msgstr "" +"RoomManager не повинен бути дочірнім об'єктом першого або другого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ " +"Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." msgstr "" +"Ð—Ð°Ð¿Ð¸Ñ Room не повинен бути дочірнім об'єктом першого або другого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ " +"Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." msgstr "" +"Ð—Ð°Ð¿Ð¸Ñ RoomGroup не повинен бути дочірнім об'єктом першого або другого Ñ€Ñ–Ð²Ð½Ñ " +"Ð´Ð»Ñ Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14183,41 +14096,83 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." msgstr "" +"Ð—Ð°Ð¿Ð¸Ñ Room не може міÑтити дочірнього об'єкта Room першого або другого Ñ€Ñ–Ð²Ð½Ñ " +"вкладеноÑті." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager не можна розташовувати у Room." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup не можна розташовувати у Room." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"Опукла оболонка кімнати міÑтить велику кількіÑть площин.\n" +"Вам варто ÑпроÑтити межу кімнати, щоб підвищити швидкодію." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager не можна розташовувати у RoomGroup." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList не надано значеннÑ." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "Вузол RoomList не може бути Spatial (або походити від Spatial)." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Ð”Ð»Ñ Â«ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð³Ð»Ð¸Ð±Ð¸Ð½Ð¸ порталу» вÑтановлено нульове значеннÑ.\n" +"Буде оброблено лише Room, у Ñкій перебуває Camera." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "У SceneTree має бути лише один Ð·Ð°Ð¿Ð¸Ñ RoomManager." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14283,7 +14238,7 @@ msgstr "Ðе знайдено анімації: «%s»" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "Скинути заÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð½Ñ–Ð¼Ð°Ñ†Ñ–Ñ—" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14461,25 +14416,30 @@ msgid "Invalid comparison function for that type." msgstr "Ðекоректна Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ€Ñ–Ð²Ð½ÑÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ типу." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Змінні величини можна пов'Ñзувати лише із функцією вузлів." +msgstr "У функції «%s» не може бути надано змінне значеннÑ." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Змінним, Ñким надано Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ функції «vertex», не можна повторно надавати " +"Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ «fragment» або «light»." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Змінним, Ñким надано Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ функції «fragment», не можна повторно " +"надавати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ «vertex» або «light»." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" msgstr "" +"ДоÑтуп до змінного Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ð° кроці фрагментації у нетиповій функції " +"неможливий!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15669,9 +15629,6 @@ msgstr "Сталі не можна змінювати." #~ msgid "I see..." #~ msgstr "Бачу..." -#~ msgid "Can't open '%s'." -#~ msgstr "Ðеможливо відкрити '%s'." - #~ msgid "Ugh" #~ msgstr "Тьху" diff --git a/editor/translations/ur_PK.po b/editor/translations/ur_PK.po index 5c5a6baa8e..fb70bc5703 100644 --- a/editor/translations/ur_PK.po +++ b/editor/translations/ur_PK.po @@ -345,6 +345,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -369,10 +370,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "سب سکریپشن بنائیں" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -939,7 +956,7 @@ msgstr "سب سکریپشن بنائیں" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2261,6 +2278,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3048,10 +3076,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3669,6 +3693,15 @@ msgid "Download from:" msgstr ".تمام کا انتخاب" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "سب سکریپشن بنائیں" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8486,6 +8519,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr ".تمام کا انتخاب" @@ -8516,6 +8555,12 @@ msgid "Remove All StyleBox Items" msgstr ".تمام کا انتخاب" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Ù¾Ø³Ù†Ø¯ÛŒØ¯Û Ø§ÙˆÙ¾Ø± منتقل کریں" @@ -12045,6 +12090,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr ".تمام کا انتخاب" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr ".تمام کا انتخاب" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13682,6 +13737,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/vi.po b/editor/translations/vi.po index 531488e640..d50d622215 100644 --- a/editor/translations/vi.po +++ b/editor/translations/vi.po @@ -355,6 +355,7 @@ msgstr "Äổi chế độ vòng lặp hoạt ảnh" msgid "Remove Anim Track" msgstr "Xóa Anim Track" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Tạo track má»›i cho %s và chèn key?" @@ -379,10 +380,27 @@ msgstr "Tạo" msgid "Anim Insert" msgstr "Chèn Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Hoạt ảnh" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer không thể tá»± tạo hoạt ảnh, phải nhá» các Player khác." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Thuá»™c tÃnh '%s' không tồn tại." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Tạo & Chèn Hoạt ảnh" @@ -954,7 +972,7 @@ msgstr "Tạo %s má»›i" msgid "No results for \"%s\"." msgstr "Không tìm thấy kết quả cho \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2299,6 +2317,17 @@ msgid "New Window" msgstr "Cá»a sổ má»›i" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Xoay khi cá»a sổ trình chỉnh sá»a được vẽ lại." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Tà i nguyên đã nháºp không thể lưu." @@ -3141,10 +3170,6 @@ msgid "Save & Restart" msgstr "Lưu & Khởi động lại" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Xoay khi cá»a sổ trình chỉnh sá»a được vẽ lại." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Cáºp nháºt Liên tục" @@ -3796,6 +3821,16 @@ msgid "Download from:" msgstr "Lá»—i tải" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Chạy trong Trình duyệt web" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Sao chép lá»—i" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8623,6 +8658,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Xóa tất cả các mục" @@ -8653,6 +8694,12 @@ msgid "Remove All StyleBox Items" msgstr "Xóa tất cả các mục" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Thêm mục Lá»›p" @@ -12282,6 +12329,16 @@ msgstr "Chỉnh chiá»u cao hình trụ" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Äặt vị trà điểm uốn" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Äặt vị trà điểm uốn" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Thay Äổi Bán KÃnh Hình Trụ" @@ -14000,6 +14057,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po index dbbd935854..8284ac605e 100644 --- a/editor/translations/zh_CN.po +++ b/editor/translations/zh_CN.po @@ -83,7 +83,7 @@ msgid "" msgstr "" "Project-Id-Version: Chinese (Simplified) (Godot Engine)\n" "POT-Creation-Date: 2018-01-20 12:15+0200\n" -"PO-Revision-Date: 2021-08-01 12:02+0000\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" "Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n" "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hans/>\n" @@ -413,6 +413,7 @@ msgstr "更改动画循环模å¼" msgid "Remove Anim Track" msgstr "移除动画轨é“" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "是å¦ä¸º %s 新建轨é“å¹¶æ’入关键帧?" @@ -437,10 +438,28 @@ msgstr "创建" msgid "Anim Insert" msgstr "æ’入动画" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "æ— æ³•æ‰“å¼€ \"%s\"。" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "动画" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ä¸èƒ½åŠ¨ç”»åŒ–è‡ªå·±ï¼Œåªå¯åŠ¨ç”»åŒ–å…¶å®ƒ Player。" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ä¸å˜åœ¨å±žæ€§â€œ%sâ€ã€‚" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "创建并æ’入动画" @@ -644,9 +663,8 @@ msgid "Go to Previous Step" msgstr "返回上一æ¥" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "é‡ç½®" +msgstr "应用é‡ç½®" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -665,9 +683,8 @@ msgid "Use Bezier Curves" msgstr "使用è´å¡žå°”曲线" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "粘贴轨é“" +msgstr "创建 RESET 轨é“" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -988,7 +1005,6 @@ msgid "Edit..." msgstr "编辑..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "跳转到方法" @@ -1008,9 +1024,9 @@ msgstr "创建 %s" msgid "No results for \"%s\"." msgstr "未找到 “%sâ€ã€‚" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "没有针对 %s çš„æè¿°ã€‚" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1110,17 +1126,15 @@ msgid "Owners Of:" msgstr "拥有者:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"是å¦ä»Žé¡¹ç›®ä¸åˆ é™¤æ‰€é€‰æ–‡ä»¶ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ï¼‰\n" -"ä½ å¯ä»¥åœ¨ç³»ç»Ÿå›žæ”¶ç«™ä¸æ¢å¤è¢«åˆ 除的文件。" +"是å¦ä»Žé¡¹ç›®ä¸åˆ é™¤æ‰€é€‰æ–‡ä»¶ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ã€‚ï¼‰\n" +"æ ¹æ®ä½ çš„æ–‡ä»¶ç³»ç»Ÿè®¾ç½®ï¼Œæ–‡ä»¶ä¼šè¢«ç§»åŠ¨è‡³ç³»ç»Ÿå›žæ”¶ç«™æˆ–æ°¸ä¹…åˆ é™¤ã€‚" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1128,9 +1142,9 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"è¦åˆ é™¤çš„æ–‡ä»¶è¢«å…¶ä»–èµ„æºæ‰€ä¾èµ–。\n" -"ä»ç„¶è¦åˆ 除å—ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ï¼‰\n" -"ä½ å¯ä»¥åœ¨ç³»ç»Ÿå›žæ”¶ç«™ä¸æ¢å¤è¢«åˆ 除的文件。" +"其它资æºéœ€è¦è¿™äº›å³å°†è¢«åˆ 除的文件æ‰èƒ½æ£å¸¸å·¥ä½œã€‚\n" +"ä»ç„¶è¦åˆ 除å—ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ã€‚ï¼‰\n" +"æ ¹æ®ä½ çš„æ–‡ä»¶ç³»ç»Ÿè®¾ç½®ï¼Œæ–‡ä»¶ä¼šè¢«ç§»åŠ¨è‡³ç³»ç»Ÿå›žæ”¶ç«™æˆ–æ°¸ä¹…åˆ é™¤ã€‚" #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1298,41 +1312,36 @@ msgid "Licenses" msgstr "许å¯è¯" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "æ‰“å¼€åŒ…æ–‡ä»¶æ—¶å‡ºé”™ï¼ˆéž ZIP æ ¼å¼ï¼‰ã€‚" +msgstr "打开“%sâ€çš„ç´ ææ–‡ä»¶æ—¶å‡ºé”™ï¼ˆéž ZIP æ ¼å¼ï¼‰ã€‚" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s(已å˜åœ¨ï¼‰" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "ç´ æâ€œ%sâ€çš„内容 - %d ä¸ªæ–‡ä»¶ä¸Žä½ çš„é¡¹ç›®å†²çªï¼š" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "ç´ æâ€œ%sâ€çš„内容 - æ²¡æœ‰æ–‡ä»¶ä¸Žä½ çš„é¡¹ç›®å†²çªï¼š" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "æ£åœ¨è§£åŽ‹ç´ æ" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "ä»¥ä¸‹æ–‡ä»¶æ— æ³•ä»ŽåŒ…ä¸æå–:" +msgstr "ä»¥ä¸‹æ–‡ä»¶æ— æ³•ä»Žç´ æâ€œ%sâ€ä¸æå–:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "以åŠå…¶å®ƒ %s 个文件。" +msgstr "(以åŠå…¶å®ƒ %s 个文件)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "软件包安装æˆåŠŸï¼" +msgstr "ç´ æâ€œ%sâ€å®‰è£…æˆåŠŸï¼" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1344,9 +1353,8 @@ msgid "Install" msgstr "安装" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "程åºåŒ…安装程åº" +msgstr "ç´ æå®‰è£…器" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1409,7 +1417,6 @@ msgid "Bypass" msgstr "æ—通" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "总线选项" @@ -1577,13 +1584,12 @@ msgid "Can't add autoload:" msgstr "æ— æ³•åŠ è½½ Autoload:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "文件ä¸å˜åœ¨ã€‚" +msgstr "%s æ˜¯æ— æ•ˆè·¯å¾„ã€‚æ–‡ä»¶ä¸å˜åœ¨ã€‚" #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s æ˜¯æ— æ•ˆè·¯å¾„ã€‚ä¸æ˜¯èµ„æºè·¯å¾„(res://)。" #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1607,9 +1613,8 @@ msgid "Name" msgstr "åç§°" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "å˜é‡" +msgstr "全局å˜é‡" #: editor/editor_data.cpp msgid "Paste Params" @@ -1775,48 +1780,47 @@ msgstr "坼入颿¿" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "å…许查看并编辑 3D 场景。" #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "å…许使用内置脚本编辑器编辑脚本。" #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "æä¾›å¯¹ç´ æåº“的内置访问。" #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "å…è®¸åœ¨åœºæ™¯é¢æ¿ä¸ç¼–辑节点层级。" #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." -msgstr "" +msgstr "å…è®¸åœ¨åœºæ™¯é¢æ¿æ“作所选节点的信å·å’Œåˆ†ç»„。" #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." -msgstr "" +msgstr "å…è®¸ä½¿ç”¨ä¸“é—¨çš„é¢æ¿æµè§ˆæœ¬åœ°æ–‡ä»¶ç³»ç»Ÿã€‚" #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." -msgstr "" +msgstr "å…许为å„ä¸ªç´ æé…ç½®å¯¼å…¥è®¾ç½®ã€‚æ–‡ä»¶ç³»ç»Ÿé¢æ¿éœ€è¦å¯ç”¨ã€‚" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" msgstr "(当å‰ï¼‰" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "ï¼ˆæ— ï¼‰" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "è¦åˆ 除当剿‰€é€‰çš„é…置文件“%sâ€å—ï¼Ÿæ— æ³•æ’¤é”€ã€‚" #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1847,19 +1851,16 @@ msgid "Enable Contextual Editor" msgstr "å¯ç”¨ä¸Šä¸‹æ–‡ç¼–辑器" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "属性:" +msgstr "类属性:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "特性" +msgstr "主è¦ç‰¹æ€§ï¼š" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "å¯ç”¨çš„类:" +msgstr "节点和类:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1876,7 +1877,6 @@ msgid "Error saving profile to path: '%s'." msgstr "å°†é…置文件ä¿å˜åˆ°è·¯å¾„ “%s†时出错。" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "é‡ç½®ä¸ºé»˜è®¤å€¼" @@ -1885,14 +1885,12 @@ msgid "Current Profile:" msgstr "当å‰é…置文件:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "åˆ é™¤é…置文件" +msgstr "创建é…置文件" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "移除图å—" +msgstr "åˆ é™¤é…置文件" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1912,18 +1910,16 @@ msgid "Export" msgstr "导出" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "当å‰é…置文件:" +msgstr "é…置所选é…置文件:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "纹ç†é€‰é¡¹" +msgstr "更多选项:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." -msgstr "" +msgstr "创建或导入é…置文件以编辑å¯ç”¨çš„类和属性。" #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1950,7 +1946,6 @@ msgid "Select Current Folder" msgstr "é€‰æ‹©å½“å‰æ–‡ä»¶å¤¹" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" msgstr "文件已å˜åœ¨ï¼Œæ˜¯å¦è¦†ç›–?" @@ -2343,6 +2338,17 @@ msgid "New Window" msgstr "新窗å£" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "编辑器窗å£é‡ç»˜æ—¶æ—‹è½¬ã€‚" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "å¯¼å…¥çš„èµ„æºæ— 法ä¿å˜ã€‚" @@ -2503,9 +2509,9 @@ msgid "" "Please read the documentation relevant to importing scenes to better " "understand this workflow." msgstr "" -"场景已被导入,所åšçš„æ›´æ”¹å°†ä¸ä¼šä¿ç•™ã€‚\n" -"请实例化或继承该场景以å…许对其进行更改。\n" -"请阅读与导入场景相关的文档,以更佳ç†è§£æ¤å·¥ä½œæµã€‚" +"该场景是被导入的场景,ä¸ä¼šä¿ç•™å¯¹å®ƒçš„æ›´æ”¹ã€‚\n" +"实例化或继承该场景就å¯ä»¥å¯¹å…¶è¿›è¡Œæ›´æ”¹ã€‚\n" +"请阅读与导入场景相关的文档,以便更好地ç†è§£æ¤å·¥ä½œæµã€‚" #: editor/editor_node.cpp msgid "" @@ -2560,14 +2566,13 @@ msgstr "是å¦åœ¨å…³é—å‰ä¿å˜å¯¹ “%s†的更改?" msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." -msgstr "" +msgstr "当å‰åœºæ™¯æ²¡æœ‰æ ¹èŠ‚ç‚¹ï¼Œä¸è¿‡ä¿å˜äº† %d 个已修改的外部资æºã€‚" #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "å¿…é¡»æœ‰æ ¹èŠ‚ç‚¹æ‰å¯ä¿å˜åœºæ™¯ã€‚" +msgstr "å¿…é¡»æœ‰æ ¹èŠ‚ç‚¹æ‰èƒ½ä¿å˜åœºæ™¯ã€‚ä½ å¯ä»¥é€šè¿‡åœºæ™¯é¢æ¿æ·»åŠ æ ¹èŠ‚ç‚¹ã€‚" #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2938,9 +2943,8 @@ msgid "Orphan Resource Explorer..." msgstr "å¤ç«‹èµ„æºæµè§ˆå™¨..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "é‡å‘½å项目" +msgstr "釿–°åŠ è½½å½“å‰é¡¹ç›®" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3088,22 +3092,20 @@ msgid "Help" msgstr "帮助" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "打开文档" +msgstr "在线文档" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "问与ç”" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "报告问题" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "设置值" +msgstr "æäº¤æ–°ç‰¹æ€§å»ºè®®" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3114,9 +3116,8 @@ msgid "Community" msgstr "社区" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "关于" +msgstr "关于 Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3168,10 +3169,6 @@ msgid "Save & Restart" msgstr "ä¿å˜å¹¶é‡å¯" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "编辑器窗å£é‡ç»˜æ—¶æ—‹è½¬ã€‚" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "æŒç»æ›´æ–°" @@ -3212,14 +3209,12 @@ msgid "Manage Templates" msgstr "ç®¡ç†æ¨¡æ¿" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" msgstr "从文件安装" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "选择æºç½‘æ ¼ï¼š" +msgstr "选择 Android æºæ–‡ä»¶" #: editor/editor_node.cpp msgid "" @@ -3299,9 +3294,8 @@ msgid "Select" msgstr "选择" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "é€‰æ‹©å½“å‰æ–‡ä»¶å¤¹" +msgstr "选择当å‰" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3336,9 +3330,8 @@ msgid "No sub-resources found." msgstr "找ä¸åˆ°å资æºã€‚" #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "找ä¸åˆ°å资æºã€‚" +msgstr "打开å资æºåˆ—表。" #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3365,12 +3358,10 @@ msgid "Update" msgstr "æ›´æ–°" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "版本:" +msgstr "版本" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" msgstr "作者" @@ -3385,14 +3376,12 @@ msgid "Measure:" msgstr "测é‡ï¼š" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "帧时间(秒)" +msgstr "帧时间(毫秒)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "平凿—¶é—´ï¼ˆç§’)" +msgstr "平凿—¶é—´ï¼ˆæ¯«ç§’)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3419,6 +3408,11 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"全部:包括该函数调用的其它函数的时间。\n" +"使用该选项寻找瓶颈。\n" +"\n" +"仅自己:åªè®¡ç®—消耗在该函数本身的时间,ä¸åŒ…å«è¯¥å‡½æ•°æ‰€è°ƒç”¨çš„其它函数。\n" +"使用该选项寻找需è¦ä¼˜åŒ–的函数。" #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3536,7 +3530,6 @@ msgid "Paste" msgstr "粘贴" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "转æ¢ä¸º %s" @@ -3586,9 +3579,8 @@ msgid "Did you forget the '_run' method?" msgstr "是å¦é—æ¼äº† _run() 方法?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." -msgstr "æŒ‰ä½ Ctrl é”®æ¥å–整。 æŒ‰ä½ Shift é”®èŽ·å–æ›´ç²¾ç¡®çš„å˜åŒ–。" +msgstr "æŒ‰ä½ %s å–æ•´ã€‚ æŒ‰ä½ Shift èŽ·å–æ›´ç²¾ç¡®çš„å˜åŒ–。" #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -3608,49 +3600,43 @@ msgstr "从节点ä¸å¯¼å…¥ï¼š" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "打开包å«è¿™äº›æ¨¡æ¿çš„æ–‡ä»¶å¤¹ã€‚" #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "å¸è½½è¿™äº›æ¨¡æ¿ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "文件 “%s†ä¸å˜åœ¨ã€‚" +msgstr "没有å¯ç”¨çš„镜åƒã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "检索镜åƒï¼Œè¯·ç‰å¾…..." +msgstr "æ£åœ¨èŽ·å–镜åƒåˆ—表……" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "æ£åœ¨å¼€å§‹ä¸‹è½½â€¦â€¦" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "请求 URL 时出错:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "æ£åœ¨è¿žæŽ¥é•œåƒç½‘ç«™..." +msgstr "æ£åœ¨è¿žæŽ¥é•œåƒâ€¦â€¦" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "æ— æ³•è§£æžä¸»æœºå:" +msgstr "æ— æ³•è§£æžè¯·æ±‚地å€ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "æ— æ³•è¿žæŽ¥åˆ°ä¸»æœºï¼š" +msgstr "æ— æ³•è¿žæŽ¥åˆ°é•œåƒã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "ä¸»æœºæ— å“应:" +msgstr "é•œåƒæ— å“应。" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3658,18 +3644,16 @@ msgid "Request failed." msgstr "请求失败。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "请求失败,é‡å®šå‘次数过多" +msgstr "请求进入了é‡å®šå‘循环。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "请求失败。" +msgstr "请求失败:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "下载完æˆï¼›æ£åœ¨è§£åŽ‹æ¨¡æ¿â€¦â€¦" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3688,13 +3672,12 @@ msgid "Error getting the list of mirrors." msgstr "获å–镜åƒåˆ—表时出错。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "è§£æžé•œåƒåˆ—表 JSON 时出错。请æäº¤æ¤é—®é¢˜ï¼" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "最佳å¯ç”¨é•œåƒ" #: editor/export_template_manager.cpp msgid "" @@ -3745,24 +3728,20 @@ msgid "SSL Handshake Error" msgstr "SSL æ¡æ‰‹é”™è¯¯" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "æ— æ³•æ‰“å¼€ ZIP 导出模æ¿ã€‚" +msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿æ–‡ä»¶ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "模æ¿ä¸çš„ version.txt æ ¼å¼æ— 效:%s。" +msgstr "导出模æ¿ä¸çš„ version.txt æ ¼å¼æ— 效:%s。" #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "模æ¿ä¸æ²¡æœ‰æ‰¾åˆ° version.txt。" +msgstr "导出模æ¿ä¸æ²¡æœ‰ version.txt。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "创建模æ¿è·¯å¾„出错:" +msgstr "为解压模æ¿åˆ›å»ºè·¯å¾„时出错:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3773,9 +3752,8 @@ msgid "Importing:" msgstr "æ£åœ¨å¯¼å…¥ï¼š" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "是å¦ç§»é™¤æ¨¡æ¿ç‰ˆæœ¬ “%sâ€ï¼Ÿ" +msgstr "是å¦ç§»é™¤æ¨¡æ¿ç‰ˆæœ¬â€œ%sâ€ï¼Ÿ" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3791,58 +3769,63 @@ msgstr "当å‰ç‰ˆæœ¬ï¼š" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." -msgstr "" +msgstr "缺失导出模æ¿ã€‚请下载或从文件安装。" #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "导出模æ¿å·²å®‰è£…就绪。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "打开文件" +msgstr "打开文件夹" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." -msgstr "" +msgstr "打开包å«å½“å‰ç‰ˆæœ¬å·²å®‰è£…模æ¿çš„æ–‡ä»¶å¤¹ã€‚" #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "å¸è½½" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "计数器åˆå§‹å€¼" +msgstr "å¸è½½å½“å‰ç‰ˆæœ¬çš„æ¨¡æ¿ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "下载错误" +msgstr "下载:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "在æµè§ˆå™¨ä¸è¿è¡Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "å¤åˆ¶é”™è¯¯ä¿¡æ¯" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "下载并安装" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." -msgstr "" +msgstr "从最佳的镜åƒä¸‹è½½å½“å‰ç‰ˆæœ¬çš„æ¨¡æ¿å¹¶å®‰è£…。" #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "开呿ž„建下官方导出模æ¿ä¸å¯ç”¨ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" msgstr "从文件安装" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "从 ZIP 文件ä¸å¯¼å…¥æ¨¡æ¿" +msgstr "从本地文件安装模æ¿ã€‚" #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3850,19 +3833,16 @@ msgid "Cancel" msgstr "å–æ¶ˆ" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "æ— æ³•æ‰“å¼€ ZIP 导出模æ¿ã€‚" +msgstr "å–æ¶ˆä¸‹è½½æ¨¡æ¿ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "已安装版本:" +msgstr "其它已安装版本:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "å¸è½½" +msgstr "å¸è½½æ¨¡æ¿" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3877,6 +3857,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"模æ¿ä¸‹è½½ä»ä¼šç»§ç»ã€‚\n" +"å®Œæˆæ—¶ä½ å¯èƒ½ä¼šæ„Ÿå—åˆ°ç¼–è¾‘å™¨çš„çŸæš‚冻结。" #: editor/filesystem_dock.cpp msgid "Favorites" @@ -3972,7 +3954,7 @@ msgstr "打开场景" #: editor/filesystem_dock.cpp msgid "Instance" -msgstr "实例" +msgstr "实例化" #: editor/filesystem_dock.cpp msgid "Add to Favorites" @@ -4019,35 +4001,32 @@ msgid "Collapse All" msgstr "全部折å " #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "æœç´¢æ–‡ä»¶" +msgstr "文件排åº" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "按å称(å‡åºï¼‰" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "按å称(é™åºï¼‰" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "按类型(å‡åºï¼‰" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "按类型(é™åºï¼‰" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "修改时间" +msgstr "按最近修改" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "修改时间" +msgstr "按最早修改" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4059,7 +4038,7 @@ msgstr "é‡å‘½å为..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "èšç„¦æœç´¢æ¡†" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4363,14 +4342,12 @@ msgid "Failed to load resource." msgstr "åŠ è½½èµ„æºå¤±è´¥ã€‚" #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "属性" +msgstr "å¤åˆ¶å±žæ€§" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "属性" +msgstr "粘贴属性" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4395,23 +4372,20 @@ msgid "Save As..." msgstr "å¦å˜ä¸º..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "ä¸åœ¨èµ„æºè·¯å¾„下。" +msgstr "更多资æºé€‰é¡¹ã€‚" #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "编辑资æºå‰ªè´´æ¿" +msgstr "编辑剪贴æ¿ä¸çš„资æº" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "å¤åˆ¶èµ„æº" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "转为内置" +msgstr "将资æºè½¬ä¸ºå†…ç½®" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4426,9 +4400,8 @@ msgid "History of recently edited objects." msgstr "最近编辑历å²å¯¹è±¡ã€‚" #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "打开文档" +msgstr "打开该对象的文档。" #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4439,9 +4412,8 @@ msgid "Filter properties" msgstr "ç›é€‰å±žæ€§" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "对象属性。" +msgstr "管ç†å¯¹è±¡å±žæ€§ã€‚" #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4684,9 +4656,8 @@ msgid "Blend:" msgstr "æ··åˆï¼š" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "傿•°å·²æ›´æ”¹" +msgstr "ä¿®æ”¹å‚æ•°ï¼š" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5408,11 +5379,11 @@ msgstr "全部" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "æœç´¢æ¨¡æ¿ã€é¡¹ç›®ã€æ¼”示" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "æœç´¢ç´ æï¼ˆä¸åŒ…嫿¨¡æ¿ã€é¡¹ç›®ã€æ¼”示)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5456,7 +5427,7 @@ msgstr "ç´ æ ZIP 文件" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "éŸ³é¢‘é¢„è§ˆæ’æ”¾/æš‚åœ" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5705,13 +5676,12 @@ msgstr "编辑锚点" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"游æˆç›¸æœºè¦†ç›–\n" -"使用编辑器视图相机覆盖游æˆç›¸æœºã€‚" +"项目相机覆盖\n" +"使用编辑器视图相机覆盖æ£åœ¨è¿è¡Œçš„项目ä¸çš„相机。" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5720,6 +5690,8 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"项目相机覆盖\n" +"没有æ£åœ¨è¿è¡Œçš„项目实例。请先在编辑器ä¸è¿è¡Œé¡¹ç›®å†ä½¿ç”¨æœ¬åŠŸèƒ½ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5785,31 +5757,25 @@ msgstr "选择模å¼" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "移除选ä¸çš„节点或过渡动画。" +msgstr "拖动:围绕ä¸å¿ƒç‚¹æ—‹è½¬æ‰€é€‰èŠ‚ç‚¹ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+拖动:移动" +msgstr "Alt+拖动:移动所选节点。" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "移除选ä¸çš„节点或过渡动画。" +msgstr "V:设置所选节点的ä¸å¿ƒç‚¹ä½ç½®ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." -msgstr "" -"æ˜¾ç¤ºé¼ æ ‡ç‚¹å‡»ä½ç½®çš„æ‰€æœ‰èŠ‚ç‚¹\n" -"ï¼ˆåŒ Alt + é¼ æ ‡å³é”®ï¼‰ã€‚" +msgstr "Alt+å³é”®ï¼šæ˜¾ç¤ºç‚¹å‡»ä½ç½®çš„æ‰€æœ‰èŠ‚ç‚¹åˆ—è¡¨ï¼ŒåŒ…å«å·²é”定节点。" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "å³é”®ï¼šåœ¨ç‚¹å‡»ä½ç½®æ·»åŠ èŠ‚ç‚¹ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6045,14 +6011,12 @@ msgid "Clear Pose" msgstr "清除姿势" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "æ·»åŠ èŠ‚ç‚¹" +msgstr "在æ¤å¤„æ·»åŠ èŠ‚ç‚¹" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "实例化场景" +msgstr "在æ¤å¤„实例化场景" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6068,49 +6032,43 @@ msgstr "平移视图" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "缩放至 3.125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "缩放至 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "缩放至 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "缩å°" +msgstr "缩放至 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "缩å°" +msgstr "缩放至 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "缩å°" +msgstr "缩放至 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "缩å°" +msgstr "缩放至 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "缩å°" +msgstr "缩放至 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "缩å°" +msgstr "缩放至 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "缩放至 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6355,9 +6313,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "æ— æ³•åˆ›å»ºå•一凸碰撞形状。" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "创建å•一凸形状" +msgstr "创建简化凸形状" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6392,9 +6349,8 @@ msgid "No mesh to debug." msgstr "没有å¯è°ƒè¯•çš„ç½‘æ ¼ã€‚" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "模型在æ¤å±‚上没有 UV" +msgstr "ç½‘æ ¼åœ¨å±‚ %d 上没有 UV。" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6458,9 +6414,8 @@ msgstr "" "这是最快(但是最ä¸ç²¾ç¡®ï¼‰çš„碰撞检测手段。" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "创建å•一凸碰撞åŒçº§" +msgstr "创建简化凸碰撞åŒçº§" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6468,20 +6423,21 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"创建简化凸碰撞åŒçº§ã€‚\n" +"与å•一碰撞形状类似,但æŸäº›æƒ…况下产生的形状更简å•,代价是牺牲准确性。" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "创建多个凸碰撞åŒçº§" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "创建基于多边形的碰撞形状。\n" -"这是性能ä½äºŽä¸Šè¿°ä¸¤ç§ä¹‹é—´çš„碰撞检测手段。" +"性能ä½äºŽå•一凸形碰撞和多边形碰撞之间。" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7117,24 +7073,20 @@ msgid "ResourcePreloader" msgstr "é¢„åŠ è½½èµ„æº" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "水平翻转" +msgstr "翻转门户" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "生æˆé¡¶ç‚¹è®¡æ•°:" +msgstr "房间生æˆç‚¹" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "生æˆé¡¶ç‚¹è®¡æ•°:" +msgstr "生æˆç‚¹" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "水平翻转" +msgstr "翻转门户" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7692,20 +7644,17 @@ msgid "None" msgstr "æ— " #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "å·ž(State)" +msgstr "旋转" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "移动:" +msgstr "平移" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "缩放:" +msgstr "缩放" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7728,52 +7677,44 @@ msgid "Animation Key Inserted." msgstr "æ’入动画键。" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "俯仰角" +msgstr "俯仰角:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "å航角:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "大å°ï¼š " +msgstr "大å°ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "绘制对象" +msgstr "绘制对象:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "æè´¨å˜æ›´" +msgstr "æè´¨å˜æ›´ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "ç€è‰²å™¨å˜æ›´" +msgstr "ç€è‰²å™¨å˜æ›´ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "表é¢å˜æ›´" +msgstr "表é¢å˜æ›´ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "绘制调用" +msgstr "绘制调用:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "顶点" +msgstr "顶点:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS:%d(%s 毫秒)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -7928,9 +7869,8 @@ msgid "Freelook Slow Modifier" msgstr "缓慢自由视图速度" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "ä¿®æ”¹æ‘„åƒæœºå°ºå¯¸" +msgstr "å¼€å…³æ‘„åƒæœºé¢„览" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -7950,9 +7890,8 @@ msgstr "" "ä¸èƒ½å馈出实际游æˆä¸çš„æ€§èƒ½ã€‚" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "转æ¢ä¸º %s" +msgstr "è½¬æ¢æˆ¿é—´" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -7973,7 +7912,6 @@ msgstr "" "åŠç眼:Gizmo 也å¯ç©¿è¿‡ä¸é€æ˜Žçš„表é¢å¯è§ï¼ˆâ€œX å…‰â€ï¼‰ã€‚" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "将节点å¸é™„至地é¢" @@ -7991,7 +7929,7 @@ msgstr "使用å¸é™„" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "ä¸ºé—¨æˆ·å‰”é™¤è½¬æ¢æˆ¿é—´ã€‚" #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8087,9 +8025,8 @@ msgid "View Grid" msgstr "æ˜¾ç¤ºç½‘æ ¼" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "视å£è®¾ç½®" +msgstr "显示门户剔除" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8409,221 +8346,194 @@ msgid "TextureRegion" msgstr "纹ç†åŒºåŸŸ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" msgstr "颜色" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" msgstr "å—体" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" msgstr "å›¾æ ‡" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "æ ·å¼" +msgstr "æ ·å¼ç›’" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} 个颜色" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "找ä¸åˆ°å资æºã€‚" +msgstr "没有颜色。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "常é‡" +msgstr "{num} 个常é‡" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "颜色常é‡ã€‚" +msgstr "没有常é‡ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} 个å—体" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "未找到ï¼" +msgstr "没有å—体。" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} ä¸ªå›¾æ ‡" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "未找到ï¼" +msgstr "æ²¡æœ‰å›¾æ ‡ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} ä¸ªæ ·å¼ç›’" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "找ä¸åˆ°å资æºã€‚" +msgstr "æ²¡æœ‰æ ·å¼ç›’。" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "当å‰é€‰ä¸ {num} 个" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "没有选ä¸å¯¼å…¥ä»»ä½•东西。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "导入主题" +msgstr "æ£åœ¨å¯¼å…¥ä¸»é¢˜é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "æ£åœ¨å¯¼å…¥é¡¹ç›® {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "确定è¦é€€å‡ºç¼–辑器å—?" +msgstr "æ£åœ¨æ›´æ–°ç¼–辑器" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "æ£åœ¨åˆ†æž" +msgstr "æ£åœ¨æ”¶å°¾" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "过滤: " +msgstr "ç›é€‰ï¼š" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "嫿•°æ®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "选择一个节点" +msgstr "按数æ®ç±»åž‹é€‰æ‹©ï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "选择一个拆分以擦除它。" +msgstr "选择所有å¯è§çš„颜色项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„颜色项目和它们的数æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„颜色项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„常é‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„常é‡é¡¹ç›®å’Œå®ƒä»¬çš„æ•°æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„常é‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„å—体项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„å—体项目和它们的数æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„å—体项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„å›¾æ ‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„å›¾æ ‡é¡¹ç›®å’Œå®ƒä»¬çš„æ•°æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "请先选择一个设置项ï¼" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„å›¾æ ‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "选择所有å¯è§çš„æ ·å¼ç›’项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„æ ·å¼ç›’项目和它们的数æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„æ ·å¼ç›’项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." -msgstr "" +msgstr "注æ„ï¼šæ·»åŠ å›¾æ ‡æ•°æ®å¯èƒ½æ˜¾è‘—å¢žåŠ ä¸»é¢˜èµ„æºçš„大å°ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "全部折å " +msgstr "折å 类型。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "全部展开" +msgstr "展开类型。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "é€‰æ‹©æ¨¡æ¿æ–‡ä»¶" +msgstr "选择所有主题项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "选择顶点" +msgstr "é€‰æ‹©å«æ•°æ®" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "选择所有主题项目åŠé¡¹ç›®æ•°æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "全选" +msgstr "å–æ¶ˆå…¨é€‰" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰ä¸»é¢˜é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "导入场景" +msgstr "导入所选项" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8631,271 +8541,241 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"导入项目选项å¡è¿˜æœ‰é€‰ä¸çš„é¡¹ç›®ã€‚å…³é—æœ¬çª—å£ä¼šä¸¢å¤±é€‰é¡¹ã€‚\n" +"ä»ç„¶å…³é—å—?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "移除所有项目" +msgstr "移除所有颜色项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "移除项目" +msgstr "é‡å‘½å项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "移除所有项目" +msgstr "移除所有常é‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "移除所有项目" +msgstr "移除所有å—体项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "移除所有项目" +msgstr "ç§»é™¤æ‰€æœ‰å›¾æ ‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "移除所有项目" +msgstr "ç§»é™¤æ‰€æœ‰æ ·å¼ç›’项目" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "æ·»åŠ ç±»é¡¹ç›®" +msgstr "æ·»åŠ é¢œè‰²é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "æ·»åŠ ç±»é¡¹ç›®" +msgstr "æ·»åŠ å¸¸é‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ å—体项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ å›¾æ ‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "æ·»åŠ æ‰€æœ‰é¡¹ç›®" +msgstr "æ·»åŠ æ ·å¼ç›’项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "移除类项目" +msgstr "é‡å‘½å颜色项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "移除类项目" +msgstr "é‡å‘½å常é‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "é‡å‘½å节点" +msgstr "é‡å‘½åå—体项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "é‡å‘½å节点" +msgstr "é‡å‘½åå›¾æ ‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "移除选ä¸é¡¹ç›®" +msgstr "é‡å‘½åæ ·å¼ç›’项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯éŸ³é¢‘总线布局。" +msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯ Theme 资æºã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸Žæ£åœ¨ç¼–辑的 Theme 资æºç›¸åŒã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "ç®¡ç†æ¨¡æ¿" +msgstr "管ç†ä¸»é¢˜é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "å¯ç¼–辑的项目" +msgstr "编辑项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" msgstr "类型:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "类型:" +msgstr "æ·»åŠ ç±»åž‹ï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ é¡¹ç›®ï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "æ·»åŠ æ‰€æœ‰é¡¹ç›®" +msgstr "æ·»åŠ æ ·å¼ç›’项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "移除项目" +msgstr "移除项目:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" msgstr "移除类项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "移除类项目" +msgstr "移除自定义项目" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "移除所有项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "GUI主题项目" +msgstr "æ·»åŠ ä¸»é¢˜é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "节点å称:" +msgstr "æ—§å称:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "导入主题" +msgstr "导入项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "默认" +msgstr "默认主题" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "编辑主题" +msgstr "编辑器主题" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "åˆ é™¤èµ„æº" +msgstr "选择其它主题资æºï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "导入主题" +msgstr "其它主题" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "é‡å‘½å轨é“" +msgstr "确认项目é‡å‘½å" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "批é‡é‡å‘½å" +msgstr "å–æ¶ˆé¡¹ç›®é‡å‘½å" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "é‡å†™" +msgstr "覆盖项目" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "å–æ¶ˆå°†æ¤æ ·å¼ç›’ç½®é¡¶ä¸ºä¸»æ ·å¼ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"å°†æ¤æ ·å¼ç›’ç½®é¡¶ä¸ºä¸»æ ·å¼ã€‚ç¼–è¾‘å…¶å±žæ€§ä¼šæ›´æ–°è¯¥ç±»åž‹ä¸‹å…¶å®ƒæ‰€æœ‰æ ·å¼ç›’的相åŒå±žæ€§ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "类型" +msgstr "æ·»åŠ ç±»åž‹" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ é¡¹ç›®ç±»åž‹" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "节点类型" +msgstr "节点类型:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "åŠ è½½é»˜è®¤" +msgstr "显示默认" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." -msgstr "" +msgstr "将默认类型项目与覆盖åŽçš„一起显示。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "é‡å†™" +msgstr "全部覆盖" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "覆盖所有默认类型项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "主题" +msgstr "主题:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "管ç†å¯¼å‡ºæ¨¡æ¿..." +msgstr "管ç†é¡¹ç›®..." #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "æ·»åŠ ã€ç§»é™¤ã€ç»„织和导入 Theme 项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "预览" +msgstr "æ·»åŠ é¢„è§ˆ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "更新预览" +msgstr "默认预览" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "选择æºç½‘æ ¼ï¼š" +msgstr "选择 UI 场景:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." -msgstr "" +msgstr "开关控件拾å–器,å¯ä»¥å¯è§†åŒ–地选择所需编辑的控件类型。" #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -8930,7 +8810,6 @@ msgid "Checked Radio Item" msgstr "已选å•选项目" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" msgstr "带å称的分隔线" @@ -8984,20 +8863,19 @@ msgstr "有, 很多, 选项" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." -msgstr "" +msgstr "æ— æ•ˆè·¯å¾„ï¼ŒPackedScene 资æºå¯èƒ½å·²è¢«ç§»åŠ¨æˆ–ç§»é™¤ã€‚" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." -msgstr "" +msgstr "æ— æ•ˆ PackedScene 资æºï¼Œæ ¹èŠ‚ç‚¹å¿…é¡»æ˜¯ Control 节点。" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯éŸ³é¢‘总线布局。" +msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯ PackedScene 资æºã€‚" #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "釿–°åŠ è½½åœºæ™¯ï¼Œåæ˜ 最新状æ€ã€‚" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10354,9 +10232,8 @@ msgid "VisualShader" msgstr "å¯è§†ç€è‰²å™¨" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "编辑å¯è§†å±žæ€§" +msgstr "编辑å¯è§†å±žæ€§ï¼š" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10480,9 +10357,8 @@ msgid "Script" msgstr "脚本" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "脚本导出模å¼:" +msgstr "GDScript 导出模å¼ï¼š" #: editor/project_export.cpp msgid "Text" @@ -10490,21 +10366,19 @@ msgstr "文本" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "编译åŽçš„å—节ç ï¼ˆåŠ è½½æ›´å¿«ï¼‰" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "åŠ å¯†ï¼ˆåœ¨ä¸‹é¢æä¾›å¯†é’¥ï¼‰" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "æ— æ•ˆçš„åŠ å¯†å¯†é’¥ï¼ˆé•¿åº¦å¿…é¡»ä¸º 64 个å—符)" +msgstr "æ— æ•ˆçš„åŠ å¯†å¯†é’¥ï¼ˆé•¿åº¦å¿…é¡»ä¸º 64 个åå…进制å—符)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "è„šæœ¬åŠ å¯†å¯†é’¥ï¼ˆ256 ä½ 16 进制ç ):" +msgstr "GDScript åŠ å¯†å¯†é’¥ï¼ˆ256 ä½åå…进制ç ):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10576,7 +10450,6 @@ msgid "Imported Project" msgstr "已导入的项目" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "项目åç§°æ— æ•ˆã€‚" @@ -10792,14 +10665,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "确定è¦åŒæ—¶è¿è¡Œ %d 个项目å—?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "从列表ä¸é€‰æ‹©è®¾å¤‡" +msgstr "是å¦ä»Žåˆ—表ä¸ç§»é™¤ %d 个项目?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "从列表ä¸é€‰æ‹©è®¾å¤‡" +msgstr "是å¦ä»Žåˆ—表ä¸ç§»é™¤è¯¥é¡¹ç›®ï¼Ÿ" #: editor/project_manager.cpp msgid "" @@ -10831,9 +10702,8 @@ msgid "Project Manager" msgstr "项目管ç†å™¨" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "项目" +msgstr "本地项目" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -10844,23 +10714,20 @@ msgid "Last Modified" msgstr "修改时间" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "导出项目" +msgstr "编辑项目" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "é‡å‘½å项目" +msgstr "è¿è¡Œé¡¹ç›®" #: editor/project_manager.cpp msgid "Scan" msgstr "扫æ" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "项目" +msgstr "扫æé¡¹ç›®" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -10871,14 +10738,12 @@ msgid "New Project" msgstr "新建项目" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "已导入的项目" +msgstr "导入项目" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "é‡å‘½å项目" +msgstr "移除项目" #: editor/project_manager.cpp msgid "Remove Missing" @@ -10889,9 +10754,8 @@ msgid "About" msgstr "关于" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "ç´ æåº“" +msgstr "ç´ æåº“项目" #: editor/project_manager.cpp msgid "Restart Now" @@ -10903,7 +10767,7 @@ msgstr "移除全部" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "åŒæ—¶åˆ é™¤é¡¹ç›®å†…å®¹ï¼ˆæ— æ³•æ’¤é”€ï¼ï¼‰" #: editor/project_manager.cpp msgid "Can't run project" @@ -10918,19 +10782,17 @@ msgstr "" "æ˜¯å¦æŸ¥çœ‹ç´ æåº“ä¸çš„官方示例项目?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "ç›é€‰å±žæ€§" +msgstr "ç›é€‰é¡¹ç›®" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"æœç´¢æ¡†æ ¹æ®å称和路径的末尾部分æ¥è¿‡æ»¤é¡¹ç›®ã€‚\n" -"å¦‚æžœè¦æ ¹æ®å称和完整路径过滤,æœç´¢å†…容应至少包å«ä¸€ä¸ª `/` å—符。" +"该æœç´¢æ¡†æ ¹æ®å称和路径的末尾部分æ¥ç›é€‰é¡¹ç›®ã€‚\n" +"å¦‚æžœè¦æ ¹æ®å称和完整路径ç›é€‰ï¼Œæœç´¢å†…容应至少包å«ä¸€ä¸ªâ€œ/â€å—符。" #: editor/project_settings_editor.cpp msgid "Key " @@ -10938,7 +10800,7 @@ msgstr "按键 " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "ç‰©ç†æŒ‰é”®" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -10984,7 +10846,7 @@ msgstr "设备" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (物ç†ï¼‰" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11126,23 +10988,20 @@ msgid "Override for Feature" msgstr "é‡å†™åŠŸèƒ½" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "æ·»åŠ ç¿»è¯‘" +msgstr "æ·»åŠ %d 个翻译" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "移除翻译" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "æ·»åŠ èµ„æºé‡å®šå‘" +msgstr "翻译资æºé‡å®šå‘ï¼šæ·»åŠ %d 个路径" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "æ·»åŠ èµ„æºé‡å®šå‘" +msgstr "翻译资æºé‡å®šå‘ï¼šæ·»åŠ %d 个é‡å®šå‘" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11581,13 +11440,13 @@ msgstr "是å¦åˆ 除节点 “%sâ€ï¼Ÿ" #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires having a scene open in the editor." -msgstr "" +msgstr "将分支ä¿å˜ä¸ºåœºæ™¯éœ€è¦ç¼–辑器打开场景。" #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." -msgstr "" +msgstr "将分支ä¿å˜ä¸ºåœºæ™¯éœ€è¦ä»…é€‰æ‹©ä¸€ä¸ªèŠ‚ç‚¹ï¼Œä½†ä½ é€‰äº† %d 个。" #: editor/scene_tree_dock.cpp msgid "" @@ -11596,6 +11455,9 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"æ— æ³•å°†æ ¹èŠ‚ç‚¹ä¿å˜ä¸ºå®žä¾‹åŒ–节点。\n" +"如果è¦åˆ›å»ºå½“å‰åœºæ™¯çš„å¯ç¼–è¾‘å‰¯æœ¬ï¼Œè¯·åœ¨æ–‡ä»¶ç³»ç»Ÿé¢æ¿çš„上下文èœå•ä¸å¤åˆ¶\n" +"或者使用场景 > 新建继承场景... 创建继承场景。" #: editor/scene_tree_dock.cpp msgid "" @@ -11603,6 +11465,9 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"æ— æ³•ä¿å˜å®žä¾‹åŒ–场景分支。\n" +"如果è¦åˆ›å»ºåœºæ™¯çš„å˜ç§ï¼Œè¯·ä½¿ç”¨åœºæ™¯ > 新建继承场景... 创建基于该实例化场景的继承" +"场景。" #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -11998,7 +11863,7 @@ msgstr "注æ„ï¼šå†…ç½®è„šæœ¬æœ‰å…¶å±€é™æ€§ï¼Œå¹¶ä¸”ä¸èƒ½ä½¿ç”¨å¤–部编辑器 msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." -msgstr "" +msgstr "è¦å‘Šï¼šè„šæœ¬å称通常ä¸èƒ½ä¸Žå†…置类型一致。" #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12070,7 +11935,7 @@ msgstr "å¤åˆ¶é”™è¯¯ä¿¡æ¯" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "在 GitHub 打开 C++ æºç " #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12142,7 +12007,7 @@ msgstr "æ ¼å¼" #: editor/script_editor_debugger.cpp msgid "Usage" -msgstr "用法" +msgstr "å 用" #: editor/script_editor_debugger.cpp msgid "Misc" @@ -12248,6 +12113,16 @@ msgstr "修改圆柱体高度" msgid "Change Ray Shape Length" msgstr "修改射线形状长度" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "è®¾ç½®æ›²çº¿çš„é¡¶ç‚¹åæ ‡" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "è®¾ç½®æ›²çº¿çš„é¡¶ç‚¹åæ ‡" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "改å˜åœ†æŸ±ä½“åŠå¾„" @@ -12357,14 +12232,12 @@ msgid "Object can't provide a length." msgstr "å¯¹è±¡æ— æ³•æä¾›é•¿åº¦ã€‚" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "å¯¼å‡ºç½‘æ ¼åº“" +msgstr "å¯¼å‡ºç½‘æ ¼ GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "导出..." +msgstr "导出 GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12404,12 +12277,11 @@ msgstr "ç½‘æ ¼åœ°å›¾ç²˜è´´æ‰€é€‰é¡¹" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Paint" -msgstr "ç»˜åˆ¶æ …æ ¼å›¾" +msgstr "ç½‘æ ¼åœ°å›¾ç»˜åˆ¶" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "ç½‘æ ¼åœ°å›¾å¡«å……æ‰€é€‰é¡¹" +msgstr "ç½‘æ ¼åœ°å›¾é€‰æ‹©" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12656,14 +12528,12 @@ msgid "Add Output Port" msgstr "å¢žåŠ è¾“å‡ºç«¯å£" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "更改类型" +msgstr "更改端å£ç±»åž‹" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "更改输入端å£åç§°" +msgstr "更改端å£åç§°" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -12774,7 +12644,6 @@ msgid "Add Preload Node" msgstr "æ·»åŠ é¢„è½½ (Preload) 节点" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" msgstr "æ·»åŠ èŠ‚ç‚¹" @@ -13034,37 +12903,31 @@ msgstr "从列表ä¸é€‰æ‹©è®¾å¤‡" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "æ£è¿è¡ŒäºŽ %d" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "全部导出" +msgstr "æ£åœ¨å¯¼å‡º APK……" #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "å¸è½½" +msgstr "æ£åœ¨å¸è½½â€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "æ£åœ¨åŠ è½½ï¼Œè¯·ç¨å€™â€¦â€¦" +msgstr "æ£åœ¨å®‰è£…到设备,请ç¨å€™â€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "æ— æ³•å®žä¾‹åŒ–åœºæ™¯ï¼" +msgstr "æ— æ³•å®‰è£…åˆ°è®¾å¤‡ï¼š%s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "执行自定义脚本..." +msgstr "æ£åœ¨è®¾å¤‡ä¸Šè¿è¡Œâ€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "æ— æ³•åˆ›å»ºæ–‡ä»¶å¤¹ã€‚" +msgstr "æ— æ³•åœ¨è®¾å¤‡ä¸Šè¿è¡Œã€‚" #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13173,40 +13036,37 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"æ— æ³•æ‰¾åˆ°â€œapksignerâ€ã€‚\n" +"请检查 Android SDK çš„ build-tools ç›®å½•ä¸æ˜¯å¦æœ‰æ¤å‘½ä»¤ã€‚\n" +"生æˆçš„ %s 未ç¾å。" #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "æ£åœ¨ç¾å调试 %s……" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"æ£åœ¨æ‰«ææ–‡ä»¶ï¼Œ\n" -"请ç¨å€™â€¦â€¦" +msgstr "æ£åœ¨ç¾åå‘布 %s……" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿ï¼š" +msgstr "找ä¸åˆ°å¯†é’¥åº“ï¼Œæ— æ³•å¯¼å‡ºã€‚" #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "“apksignerâ€è¿”回错误 #%d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "æ£åœ¨æ·»åŠ %s..." +msgstr "æ£åœ¨æ ¡éªŒ %s……" #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "“apksignerâ€æ ¡éªŒ %s 失败。" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "全部导出" +msgstr "æ£åœ¨ä¸º Android 导出" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13222,7 +13082,7 @@ msgstr "æ— æ•ˆæ–‡ä»¶åï¼Android APK 必须有 *.apk 扩展。" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "䏿”¯æŒçš„å¯¼å‡ºæ ¼å¼ï¼\n" #: platform/android/export/export.cpp msgid "" @@ -13246,17 +13106,15 @@ msgstr "" #: platform/android/export/export.cpp msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" -msgstr "" +msgstr "æ— æ³•ä½¿ç”¨é¡¹ç›®å称覆盖 res://android/build/res/*.xml 文件" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "æ— æ³•åœ¨é¡¹ç›®ç›®å½•ä¸‹æ‰¾åˆ°project.godot文件。" +msgstr "æ— æ³•å°†é¡¹ç›®æ–‡ä»¶å¯¼å‡ºè‡³ gradle 项目\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" +msgstr "æ— æ³•å†™å…¥æ‰©å±•åŒ…æ–‡ä»¶ï¼" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13281,21 +13139,20 @@ msgid "" msgstr "æ— æ³•å¤åˆ¶ä¸Žæ›´å导出文件,请在 Gradle 项目文件夹内确认输出。" #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "没有动画: “%sâ€" +msgstr "包ä¸å˜åœ¨ï¼š%s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "æ£åœ¨åˆ›å»ºè½®å»“..." +msgstr "æ£åœ¨åˆ›å»º APK……" #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿ï¼š" +msgstr "" +"找ä¸åˆ°å¯¼å‡ºæ¨¡æ¿ APK:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13304,24 +13161,24 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"导出模æ¿ç¼ºå¤±æ‰€é€‰æž¶æž„的库:%s。\n" +"请使用全部所需的库构建模æ¿ï¼Œæˆ–者在导出预设ä¸å–消对缺失架构的选择。" #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "æ£åœ¨æ·»åŠ %s..." +msgstr "æ£åœ¨æ·»åŠ æ–‡ä»¶â€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" +msgstr "æ— æ³•å¯¼å‡ºé¡¹ç›®æ–‡ä»¶" #: platform/android/export/export.cpp msgid "Aligning APK..." -msgstr "å¯¹é½ APK..." +msgstr "æ£åœ¨å¯¹é½ APK……" #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "æ— æ³•è§£åŽ‹æœªå¯¹é½çš„临时 APK。" #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13368,45 +13225,40 @@ msgid "Could not write file:" msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" +msgstr "æ— æ³•è¯»å–æ–‡ä»¶ï¼š" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "æ— æ³•è¯»å–自定义 HTML 壳层:" +msgstr "æ— æ³•è¯»å– HTML 壳:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "æ— æ³•åˆ›å»ºæ–‡ä»¶å¤¹ã€‚" +msgstr "æ— æ³•åˆ›å»º HTTP æœåŠ¡å™¨ç›®å½•ï¼š" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "ä¿å˜åœºæ™¯å‡ºé”™ã€‚" +msgstr "å¯åЍ HTTP æœåŠ¡å™¨æ—¶å‡ºé”™ï¼š" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "æ— æ•ˆçš„æ ‡è¯†ç¬¦ï¼š" +msgstr "æ— æ•ˆçš„åŒ…æ ‡è¯†ç¬¦ï¼š" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "å…¬è¯ï¼šéœ€è¦ä»£ç ç¾å。" #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "å…¬è¯ï¼šéœ€è¦åŠ å¼ºçš„è¿è¡Œæ—¶çŽ¯å¢ƒã€‚" #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "å…¬è¯ï¼šæœªæŒ‡å®š Apple ID å称。" #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "å…¬è¯ï¼šæœªæŒ‡å®š Apple ID 密ç 。" #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -13799,6 +13651,8 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"å› ä¸ºå˜åœ¨å·²çŸ¥é—®é¢˜ï¼ŒGIProbe Compress 属性已被å¯ç”¨ï¼Œä¸ä¼šå†èµ·ä»»ä½•作用。\n" +"如果è¦ç§»é™¤æœ¬è¦å‘Šï¼Œè¯·ç¦ç”¨ GIProbe çš„ Compress 属性。" #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -13882,15 +13736,15 @@ msgstr "Node A 与 Node B 必须为ä¸åŒçš„ PhysicsBody" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomManager ä¸åº”该是 Portal çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Room ä¸åº”该是 Portal çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomGroup ä¸åº”该是 Portal çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/remote_transform.cpp msgid "" @@ -13901,42 +13755,82 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." -msgstr "" +msgstr "Room ä¸èƒ½æ˜¯å¦ä¸€ä¸ª Room çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager ä¸åº”该被放置在 Room ä¸ã€‚" #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup ä¸åº”该被放置在 Room ä¸ã€‚" #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"房间的凸包ä¸åŒ…å«å¤§é‡å¹³é¢ã€‚\n" +"为了æå‡æ€§èƒ½ï¼Œè¯·è€ƒè™‘简化房间的边界。" #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager ä¸åº”该被放置在 RoomGroup ä¸ã€‚" #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList 尚未赋值。" #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "RoomList 节点应该是 Spatial(或者派生自 Spatial)。" #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Portal Depth Limit 被设置为零。\n" +"ä»…ä¼šæ¸²æŸ“æ‘„åƒæœºæ‰€åœ¨æˆ¿é—´ã€‚" #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "åœºæ™¯æ ‘ä¸ä»…能å˜åœ¨ä¸€ä¸ª RoomManager。" + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -13999,7 +13893,7 @@ msgstr "没有动画: “%sâ€" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "动画应用é‡ç½®" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14165,25 +14059,24 @@ msgid "Invalid comparison function for that type." msgstr "è¯¥ç±»åž‹çš„æ¯”è¾ƒåŠŸèƒ½æ— æ•ˆã€‚" #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "å˜é‡åªèƒ½åœ¨é¡¶ç‚¹å‡½æ•°ä¸æŒ‡å®šã€‚" +msgstr "Varying ä¸èƒ½åœ¨â€œ%sâ€å‡½æ•°ä¸èµ‹å€¼ã€‚" #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." -msgstr "" +msgstr "已在“vertexâ€å‡½æ•°ä¸èµ‹å€¼çš„ varying ä¸èƒ½åœ¨â€œfragmentâ€æˆ–“lightâ€ä¸é‡æ–°èµ‹å€¼ã€‚" #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." -msgstr "" +msgstr "已在“fragmentâ€å‡½æ•°ä¸èµ‹å€¼çš„ varying ä¸èƒ½åœ¨â€œvertexâ€æˆ–“lightâ€ä¸é‡æ–°èµ‹å€¼ã€‚" #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" -msgstr "" +msgstr "ä¸èƒ½åœ¨è‡ªå®šä¹‰å‡½æ•°ä¸è®¿é—®ç‰‡æ®µ varyingï¼" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15184,9 +15077,6 @@ msgstr "ä¸å…许修改常é‡ã€‚" #~ msgid "I see..." #~ msgstr "好å§..." -#~ msgid "Can't open '%s'." -#~ msgstr "æ— æ³•æ‰“å¼€ \"%s\"。" - #~ msgid "Ugh" #~ msgstr "呃" diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po index 9cae040eb4..e5327f79d9 100644 --- a/editor/translations/zh_HK.po +++ b/editor/translations/zh_HK.po @@ -361,6 +361,7 @@ msgstr "更改動畫循環" msgid "Remove Anim Track" msgstr "移除動畫軌跡" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp #, fuzzy msgid "Create NEW track for %s and insert key?" @@ -389,10 +390,28 @@ msgstr "新增" msgid "Anim Insert" msgstr "æ’入動畫" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "ä¸èƒ½é€£æŽ¥ã€‚" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "新增動畫" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "內容" + #: editor/animation_track_editor.cpp #, fuzzy msgid "Anim Create & Insert" @@ -990,7 +1009,7 @@ msgstr "新增" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2367,6 +2386,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3209,10 +3239,6 @@ msgid "Save & Restart" msgstr "å„²å˜æª”案" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "連續" @@ -3873,6 +3899,16 @@ msgid "Download from:" msgstr "下載出ç¾éŒ¯èª¤" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ç€è¦½" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "載入錯誤" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8886,6 +8922,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "移除é¸é …" @@ -8916,6 +8958,12 @@ msgid "Remove All StyleBox Items" msgstr "移除é¸é …" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "åŠ åˆ°æœ€æ„›" @@ -12581,6 +12629,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "åªé™é¸ä¸" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "åªé™é¸ä¸" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14267,6 +14325,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14917,10 +15011,6 @@ msgstr "" #~ msgstr "多è¬!" #, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "ä¸èƒ½é€£æŽ¥ã€‚" - -#, fuzzy #~ msgid "Ugh" #~ msgstr "å—¯......" diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po index 2b4dd0fc03..4fc48abd03 100644 --- a/editor/translations/zh_TW.po +++ b/editor/translations/zh_TW.po @@ -360,6 +360,7 @@ msgstr "更改動畫循環模å¼" msgid "Remove Anim Track" msgstr "刪除動畫軌" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "確定è¦ç‚º %s 建立動畫軌並æ’入關éµç•«æ ¼å—Žï¼Ÿ" @@ -384,10 +385,28 @@ msgstr "建立" msgid "Anim Insert" msgstr "æ’入動畫" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "無法開啟 \"%s\"。" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "å‹•ç•«" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ä¸èƒ½æ’放自己,åªå¯æ’放其他 Player。" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "屬性「%sã€ä¸å˜åœ¨ã€‚" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "新增並æ’入動畫" @@ -955,7 +974,7 @@ msgstr "建立新的 %s" msgid "No results for \"%s\"." msgstr "找ä¸åˆ°èˆ‡ã€Œ%sã€ç›¸é—œçš„çµæžœã€‚" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2292,6 +2311,17 @@ msgid "New Window" msgstr "新視窗" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ç·¨è¼¯å™¨è¦–çª—é‡æ–°ç¹ªè£½æ™‚旋轉。" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "匯入的資æºç„¡æ³•ä¿å˜ã€‚" @@ -3115,10 +3145,6 @@ msgid "Save & Restart" msgstr "ä¿å˜ä¸¦é‡æ–°å•Ÿå‹•" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ç·¨è¼¯å™¨è¦–çª—é‡æ–°ç¹ªè£½æ™‚旋轉。" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "æŒçºŒæ›´æ–°" @@ -3768,6 +3794,16 @@ msgid "Download from:" msgstr "下載錯誤" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "在ç€è¦½å™¨ä¸åŸ·è¡Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "複製錯誤" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8581,6 +8617,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ç§»é™¤æ‰€æœ‰é …ç›®" @@ -8611,6 +8653,12 @@ msgid "Remove All StyleBox Items" msgstr "ç§»é™¤æ‰€æœ‰é …ç›®" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "æ–°å¢žé¡žåˆ¥é …ç›®" @@ -12194,6 +12242,16 @@ msgstr "更改圓柱形高度" msgid "Change Ray Shape Length" msgstr "更改射線形長度" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "è¨å®šæ›²ç·šæŽ§åˆ¶é»žä½ç½®" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "è¨å®šæ›²ç·šæŽ§åˆ¶é»žä½ç½®" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "更改圓柱體åŠå¾‘" @@ -13887,6 +13945,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "該形體在è¨å®šç¶²æ ¼å‰éƒ½å°‡è¢«å¿½ç•¥ã€‚" @@ -14806,9 +14900,6 @@ msgstr "ä¸å¯ä¿®æ”¹å¸¸æ•¸ã€‚" #~ msgid "Thanks!" #~ msgstr "è¬è¬!" -#~ msgid "Can't open '%s'." -#~ msgstr "無法開啟 \"%s\"。" - #~ msgid "Ugh" #~ msgstr "呃" diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 91465bcfea..bf11cc7f68 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -88,36 +88,40 @@ uint32_t CSGShape3D::get_collision_mask() const { return collision_mask; } -void CSGShape3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void CSGShape3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool CSGShape3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CSGShape3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CSGShape3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void CSGShape3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool CSGShape3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CSGShape3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool CSGShape3D::is_root_shape() const { @@ -605,11 +609,11 @@ void CSGShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents); ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents); diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 5814ce4c83..5cf371665e 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -137,11 +137,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_snap(float p_snap); float get_snap() const; diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index f42ce8c379..446269f3f0 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -9,18 +9,18 @@ <tutorials> </tutorials> <methods> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_meshes" qualifiers="const"> @@ -35,20 +35,20 @@ Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 8ea7384658..c1dbe63628 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -53,18 +53,18 @@ The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the [member collision_layer]. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the [member collision_mask]. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_meshes"> @@ -119,20 +119,20 @@ <description> </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets an individual bit on the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets an individual bit on the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="world_to_map" qualifiers="const"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index fea513c820..8e8b6f14ad 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -151,36 +151,40 @@ uint32_t GridMap::get_collision_mask() const { return collision_mask; } -void GridMap::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void GridMap::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool GridMap::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool GridMap::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void GridMap::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void GridMap::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool GridMap::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool GridMap::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void GridMap::set_bake_navigation(bool p_bake_navigation) { @@ -794,11 +798,11 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &GridMap::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &GridMap::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &GridMap::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &GridMap::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &GridMap::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &GridMap::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &GridMap::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &GridMap::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &GridMap::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &GridMap::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation); ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 8cd82e1f4c..879489fc70 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -217,11 +217,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_bake_navigation(bool p_bake_navigation); bool is_baking_navigation(); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index a802e8022d..c37fbb6bb3 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -2323,10 +2323,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da pset.instantiate(); pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); pset->set_base_type(obj->get_class()); - /*if (use_value) { - pset->set_use_builtin_value(true); - pset->set_builtin_value(d["value"]); - }*/ vnode = pset; } else { Ref<VisualScriptPropertyGet> pget; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 1795bbe523..fc86abb6f1 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -30,3021 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/image_loader.h" -#include "core/io/json.h" -#include "core/io/marshalls.h" -#include "core/io/zip_io.h" -#include "core/os/os.h" -#include "core/templates/safe_refcount.h" -#include "core/version.h" -#include "drivers/png/png_driver_common.h" -#include "editor/editor_export.h" -#include "editor/editor_log.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "main/splash.gen.h" -#include "platform/android/export/gradle_export_util.h" -#include "platform/android/logo.gen.h" -#include "platform/android/plugin/godot_plugin_config.h" -#include "platform/android/run_icon.gen.h" - -#include <string.h> - -static const char *android_perms[] = { - "ACCESS_CHECKIN_PROPERTIES", - "ACCESS_COARSE_LOCATION", - "ACCESS_FINE_LOCATION", - "ACCESS_LOCATION_EXTRA_COMMANDS", - "ACCESS_MOCK_LOCATION", - "ACCESS_NETWORK_STATE", - "ACCESS_SURFACE_FLINGER", - "ACCESS_WIFI_STATE", - "ACCOUNT_MANAGER", - "ADD_VOICEMAIL", - "AUTHENTICATE_ACCOUNTS", - "BATTERY_STATS", - "BIND_ACCESSIBILITY_SERVICE", - "BIND_APPWIDGET", - "BIND_DEVICE_ADMIN", - "BIND_INPUT_METHOD", - "BIND_NFC_SERVICE", - "BIND_NOTIFICATION_LISTENER_SERVICE", - "BIND_PRINT_SERVICE", - "BIND_REMOTEVIEWS", - "BIND_TEXT_SERVICE", - "BIND_VPN_SERVICE", - "BIND_WALLPAPER", - "BLUETOOTH", - "BLUETOOTH_ADMIN", - "BLUETOOTH_PRIVILEGED", - "BRICK", - "BROADCAST_PACKAGE_REMOVED", - "BROADCAST_SMS", - "BROADCAST_STICKY", - "BROADCAST_WAP_PUSH", - "CALL_PHONE", - "CALL_PRIVILEGED", - "CAMERA", - "CAPTURE_AUDIO_OUTPUT", - "CAPTURE_SECURE_VIDEO_OUTPUT", - "CAPTURE_VIDEO_OUTPUT", - "CHANGE_COMPONENT_ENABLED_STATE", - "CHANGE_CONFIGURATION", - "CHANGE_NETWORK_STATE", - "CHANGE_WIFI_MULTICAST_STATE", - "CHANGE_WIFI_STATE", - "CLEAR_APP_CACHE", - "CLEAR_APP_USER_DATA", - "CONTROL_LOCATION_UPDATES", - "DELETE_CACHE_FILES", - "DELETE_PACKAGES", - "DEVICE_POWER", - "DIAGNOSTIC", - "DISABLE_KEYGUARD", - "DUMP", - "EXPAND_STATUS_BAR", - "FACTORY_TEST", - "FLASHLIGHT", - "FORCE_BACK", - "GET_ACCOUNTS", - "GET_PACKAGE_SIZE", - "GET_TASKS", - "GET_TOP_ACTIVITY_INFO", - "GLOBAL_SEARCH", - "HARDWARE_TEST", - "INJECT_EVENTS", - "INSTALL_LOCATION_PROVIDER", - "INSTALL_PACKAGES", - "INSTALL_SHORTCUT", - "INTERNAL_SYSTEM_WINDOW", - "INTERNET", - "KILL_BACKGROUND_PROCESSES", - "LOCATION_HARDWARE", - "MANAGE_ACCOUNTS", - "MANAGE_APP_TOKENS", - "MANAGE_DOCUMENTS", - "MASTER_CLEAR", - "MEDIA_CONTENT_CONTROL", - "MODIFY_AUDIO_SETTINGS", - "MODIFY_PHONE_STATE", - "MOUNT_FORMAT_FILESYSTEMS", - "MOUNT_UNMOUNT_FILESYSTEMS", - "NFC", - "PERSISTENT_ACTIVITY", - "PROCESS_OUTGOING_CALLS", - "READ_CALENDAR", - "READ_CALL_LOG", - "READ_CONTACTS", - "READ_EXTERNAL_STORAGE", - "READ_FRAME_BUFFER", - "READ_HISTORY_BOOKMARKS", - "READ_INPUT_STATE", - "READ_LOGS", - "READ_PHONE_STATE", - "READ_PROFILE", - "READ_SMS", - "READ_SOCIAL_STREAM", - "READ_SYNC_SETTINGS", - "READ_SYNC_STATS", - "READ_USER_DICTIONARY", - "REBOOT", - "RECEIVE_BOOT_COMPLETED", - "RECEIVE_MMS", - "RECEIVE_SMS", - "RECEIVE_WAP_PUSH", - "RECORD_AUDIO", - "REORDER_TASKS", - "RESTART_PACKAGES", - "SEND_RESPOND_VIA_MESSAGE", - "SEND_SMS", - "SET_ACTIVITY_WATCHER", - "SET_ALARM", - "SET_ALWAYS_FINISH", - "SET_ANIMATION_SCALE", - "SET_DEBUG_APP", - "SET_ORIENTATION", - "SET_POINTER_SPEED", - "SET_PREFERRED_APPLICATIONS", - "SET_PROCESS_LIMIT", - "SET_TIME", - "SET_TIME_ZONE", - "SET_WALLPAPER", - "SET_WALLPAPER_HINTS", - "SIGNAL_PERSISTENT_PROCESSES", - "STATUS_BAR", - "SUBSCRIBED_FEEDS_READ", - "SUBSCRIBED_FEEDS_WRITE", - "SYSTEM_ALERT_WINDOW", - "TRANSMIT_IR", - "UNINSTALL_SHORTCUT", - "UPDATE_DEVICE_STATS", - "USE_CREDENTIALS", - "USE_SIP", - "VIBRATE", - "WAKE_LOCK", - "WRITE_APN_SETTINGS", - "WRITE_CALENDAR", - "WRITE_CALL_LOG", - "WRITE_CONTACTS", - "WRITE_EXTERNAL_STORAGE", - "WRITE_GSERVICES", - "WRITE_HISTORY_BOOKMARKS", - "WRITE_PROFILE", - "WRITE_SECURE_SETTINGS", - "WRITE_SETTINGS", - "WRITE_SMS", - "WRITE_SOCIAL_STREAM", - "WRITE_SYNC_SETTINGS", - "WRITE_USER_DICTIONARY", - nullptr -}; - -static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png"; -static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png"; -static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png"; -static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png"; -static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml"; -static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json"; - -const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/splash_bg_color" /> - <item> - <bitmap - android:gravity="center" - android:filter="%s" - android:src="@drawable/splash" /> - </item> -</layer-list> -)SPLASH"; - -struct LauncherIcon { - const char *export_path; - int dimensions = 0; -}; - -static const int icon_densities_count = 6; -static const char *launcher_icon_option = "launcher_icons/main_192x192"; -static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432"; -static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432"; - -static const LauncherIcon launcher_icons[icon_densities_count] = { - { "res/mipmap-xxxhdpi-v4/icon.png", 192 }, - { "res/mipmap-xxhdpi-v4/icon.png", 144 }, - { "res/mipmap-xhdpi-v4/icon.png", 96 }, - { "res/mipmap-hdpi-v4/icon.png", 72 }, - { "res/mipmap-mdpi-v4/icon.png", 48 }, - { "res/mipmap/icon.png", 192 } -}; - -static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = { - { "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 }, - { "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 }, - { "res/mipmap-xhdpi-v4/icon_foreground.png", 216 }, - { "res/mipmap-hdpi-v4/icon_foreground.png", 162 }, - { "res/mipmap-mdpi-v4/icon_foreground.png", 108 }, - { "res/mipmap/icon_foreground.png", 432 } -}; - -static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = { - { "res/mipmap-xxxhdpi-v4/icon_background.png", 432 }, - { "res/mipmap-xxhdpi-v4/icon_background.png", 324 }, - { "res/mipmap-xhdpi-v4/icon_background.png", 216 }, - { "res/mipmap-hdpi-v4/icon_background.png", 162 }, - { "res/mipmap-mdpi-v4/icon_background.png", 108 }, - { "res/mipmap/icon_background.png", 432 } -}; - -static const int EXPORT_FORMAT_APK = 0; -static const int EXPORT_FORMAT_AAB = 1; - -class EditorExportPlatformAndroid : public EditorExportPlatform { - GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform); - - Ref<ImageTexture> logo; - Ref<ImageTexture> run_icon; - - struct Device { - String id; - String name; - String description; - int api_level = 0; - }; - - struct APKExportData { - zipFile apk; - EditorProgress *ep = nullptr; - }; - - struct CustomExportData { - bool debug; - Vector<String> libs; - }; - - Vector<PluginConfigAndroid> plugins; - String last_plugin_names; - uint64_t last_custom_build_time = 0; - SafeFlag plugins_changed; - Mutex plugins_lock; - Vector<Device> devices; - SafeFlag devices_changed; - Mutex device_lock; - Thread check_for_changes_thread; - SafeFlag quit_request; - - static void _check_for_changes_poll_thread(void *ud) { - EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; - - while (!ea->quit_request.is_set()) { - // Check for plugins updates - { - // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed.is_set()) { - Vector<PluginConfigAndroid> loaded_plugins = get_plugins(); - - MutexLock lock(ea->plugins_lock); - - if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed.set(); - } else { - for (int i = 0; i < ea->plugins.size(); i++) { - if (ea->plugins[i].name != loaded_plugins[i].name) { - ea->plugins_changed.set(); - break; - } - } - } - - if (ea->plugins_changed.is_set()) { - ea->plugins = loaded_plugins; - } - } - } - - // Check for devices updates - String adb = get_adb_path(); - if (FileAccess::exists(adb)) { - String devices; - List<String> args; - args.push_back("devices"); - int ec; - OS::get_singleton()->execute(adb, args, &devices, &ec); - - Vector<String> ds = devices.split("\n"); - Vector<String> ldevices; - for (int i = 1; i < ds.size(); i++) { - String d = ds[i]; - int dpos = d.find("device"); - if (dpos == -1) { - continue; - } - d = d.substr(0, dpos).strip_edges(); - ldevices.push_back(d); - } - - MutexLock lock(ea->device_lock); - - bool different = false; - - if (ea->devices.size() != ldevices.size()) { - different = true; - } else { - for (int i = 0; i < ea->devices.size(); i++) { - if (ea->devices[i].id != ldevices[i]) { - different = true; - break; - } - } - } - - if (different) { - Vector<Device> ndevices; - - for (int i = 0; i < ldevices.size(); i++) { - Device d; - d.id = ldevices[i]; - for (int j = 0; j < ea->devices.size(); j++) { - if (ea->devices[j].id == ldevices[i]) { - d.description = ea->devices[j].description; - d.name = ea->devices[j].name; - d.api_level = ea->devices[j].api_level; - } - } - - if (d.description == "") { - //in the oven, request! - args.clear(); - args.push_back("-s"); - args.push_back(d.id); - args.push_back("shell"); - args.push_back("getprop"); - int ec2; - String dp; - - OS::get_singleton()->execute(adb, args, &dp, &ec2); - - Vector<String> props = dp.split("\n"); - String vendor; - String device; - d.description = "Device ID: " + d.id + "\n"; - d.api_level = 0; - for (int j = 0; j < props.size(); j++) { - // got information by `shell cat /system/build.prop` before and its format is "property=value" - // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above - // its format is "[property]: [value]" so changed it as like build.prop - String p = props[j]; - p = p.replace("]: ", "="); - p = p.replace("[", ""); - p = p.replace("]", ""); - - if (p.begins_with("ro.product.model=")) { - device = p.get_slice("=", 1).strip_edges(); - } else if (p.begins_with("ro.product.brand=")) { - vendor = p.get_slice("=", 1).strip_edges().capitalize(); - } else if (p.begins_with("ro.build.display.id=")) { - d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.build.version.release=")) { - d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.build.version.sdk=")) { - d.api_level = p.get_slice("=", 1).to_int(); - } else if (p.begins_with("ro.product.cpu.abi=")) { - d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.product.manufacturer=")) { - d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.board.platform=")) { - d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.opengles.version=")) { - uint32_t opengl = p.get_slice("=", 1).to_int(); - d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n"; - } - } - - d.name = vendor + " " + device; - if (device == String()) { - continue; - } - } - - ndevices.push_back(d); - } - - ea->devices = ndevices; - ea->devices_changed.set(); - } - } - - uint64_t sleep = 200; - uint64_t wait = 3000000; - uint64_t time = OS::get_singleton()->get_ticks_usec(); - while (OS::get_singleton()->get_ticks_usec() - time < wait) { - OS::get_singleton()->delay_usec(1000 * sleep); - if (ea->quit_request.is_set()) { - break; - } - } - } - - if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { - String adb = get_adb_path(); - if (!FileAccess::exists(adb)) { - return; //adb not configured - } - - List<String> args; - args.push_back("kill-server"); - OS::get_singleton()->execute(adb, args); - }; - } - - String get_project_name(const String &p_name) const { - String aname; - if (p_name != "") { - aname = p_name; - } else { - aname = ProjectSettings::get_singleton()->get("application/config/name"); - } - - if (aname == "") { - aname = VERSION_NAME; - } - - return aname; - } - - String get_package_name(const String &p_package) const { - String pname = p_package; - String basename = ProjectSettings::get_singleton()->get("application/config/name"); - basename = basename.to_lower(); - - String name; - bool first = true; - for (int i = 0; i < basename.length(); i++) { - char32_t c = basename[i]; - if (c >= '0' && c <= '9' && first) { - continue; - } - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { - name += String::chr(c); - first = false; - } - } - if (name == "") { - name = "noname"; - } - - pname = pname.replace("$genname", name); - - return pname; - } - - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Package name is missing."); - } - return false; - } - - int segments = 0; - bool first = true; - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (first && c == '.') { - if (r_error) { - *r_error = TTR("Package segments must be of non-zero length."); - } - return false; - } - if (c == '.') { - segments++; - first = true; - continue; - } - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c)); - } - return false; - } - if (first && (c >= '0' && c <= '9')) { - if (r_error) { - *r_error = TTR("A digit cannot be the first character in a package segment."); - } - return false; - } - if (first && c == '_') { - if (r_error) { - *r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c)); - } - return false; - } - first = false; - } - - if (segments == 0) { - if (r_error) { - *r_error = TTR("The package must have at least one '.' separator."); - } - return false; - } - - if (first) { - if (r_error) { - *r_error = TTR("Package segments must be of non-zero length."); - } - return false; - } - - return true; - } - - static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected attime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ - - // -- Unconditional uncompress to mimic AAPT plus some other - - static const char *unconditional_compress_ext[] = { - // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp - // These formats are already compressed, or don't compress well: - ".jpg", ".jpeg", ".png", ".gif", - ".wav", ".mp2", ".mp3", ".ogg", ".aac", - ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", - ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", - ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", - // Godot-specific: - ".webp", // Same reasoning as .png - ".cfb", // Don't let small config files slow-down startup - ".scn", // Binary scenes are usually already compressed - ".stex", // Streamable textures are usually already compressed - // Trailer for easier processing - nullptr - }; - - for (const char **ext = unconditional_compress_ext; *ext; ++ext) { - if (p_path.to_lower().ends_with(String(*ext))) { - return false; - } - } - - // -- Compressed resource? - - if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { - // Already compressed - return false; - } - - // --- TODO: Decide on texture resources according to their image compression setting - - return true; - } - - static zip_fileinfo get_zip_fileinfo() { - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - zipfi.external_fa = 0; - zipfi.internal_fa = 0; - - return zipfi; - } - - static Vector<String> get_abis() { - Vector<String> abis; - abis.push_back("armeabi-v7a"); - abis.push_back("arm64-v8a"); - abis.push_back("x86"); - abis.push_back("x86_64"); - return abis; - } - - /// List the gdap files in the directory specified by the p_path parameter. - static Vector<String> list_gdap_files(const String &p_path) { - Vector<String> dir_files; - DirAccessRef da = DirAccess::open(p_path); - if (da) { - da->list_dir_begin(); - while (true) { - String file = da->get_next(); - if (file == "") { - break; - } - - if (da->current_is_dir() || da->current_is_hidden()) { - continue; - } - - if (file.ends_with(PluginConfigAndroid::PLUGIN_CONFIG_EXT)) { - dir_files.push_back(file); - } - } - da->list_dir_end(); - } - - return dir_files; - } - - static Vector<PluginConfigAndroid> get_plugins() { - Vector<PluginConfigAndroid> loaded_plugins; - - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins"); - - // Add the prebuilt plugins - loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir)); - - if (DirAccess::exists(plugins_dir)) { - Vector<String> plugins_filenames = list_gdap_files(plugins_dir); - - if (!plugins_filenames.is_empty()) { - Ref<ConfigFile> config_file = memnew(ConfigFile); - for (int i = 0; i < plugins_filenames.size(); i++) { - PluginConfigAndroid config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); - if (config.valid_config) { - loaded_plugins.push_back(config); - } else { - print_error("Invalid plugin config file " + plugins_filenames[i]); - } - } - } - } - - return loaded_plugins; - } - - static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { - Vector<PluginConfigAndroid> enabled_plugins; - Vector<PluginConfigAndroid> all_plugins = get_plugins(); - for (int i = 0; i < all_plugins.size(); i++) { - PluginConfigAndroid plugin = all_plugins[i]; - bool enabled = p_presets->get("plugins/" + plugin.name); - if (enabled) { - enabled_plugins.push_back(plugin); - } - } - - return enabled_plugins; - } - - static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) { - zip_fileinfo zipfi = get_zip_fileinfo(); - zipOpenNewFileInZip(ed->apk, - p_path.utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - compression_method, - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size()); - zipCloseFileInZip(ed->apk); - - return OK; - } - - static Error save_apk_so(void *p_userdata, const SharedObject &p_so) { - if (!p_so.path.get_file().begins_with("lib")) { - String err = "Android .so file names must start with \"lib\", but got: " + p_so.path; - ERR_PRINT(err); - return FAILED; - } - APKExportData *ed = (APKExportData *)p_userdata; - Vector<String> abis = get_abis(); - bool exported = false; - for (int i = 0; i < p_so.tags.size(); ++i) { - // shared objects can be fat (compatible with multiple ABIs) - int abi_index = abis.find(p_so.tags[i]); - if (abi_index != -1) { - exported = true; - String abi = abis[abi_index]; - String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file()); - Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); - Error store_err = store_in_apk(ed, dst_path, array); - ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'."); - } - } - if (!exported) { - String abis_string = String(" ").join(abis); - String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; - ERR_PRINT(err); - return FAILED; - } - return OK; - } - - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - APKExportData *ed = (APKExportData *)p_userdata; - String dst_path = p_path.replace_first("res://", "assets/"); - - store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); - return OK; - } - - static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - return OK; - } - - static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so) { - ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED, - "Android .so file names must start with \"lib\", but got: " + p_so.path); - Vector<String> abis = get_abis(); - CustomExportData *export_data = (CustomExportData *)p_userdata; - bool exported = false; - for (int i = 0; i < p_so.tags.size(); ++i) { - int abi_index = abis.find(p_so.tags[i]); - if (abi_index != -1) { - exported = true; - String base = "res://android/build/libs"; - String type = export_data->debug ? "debug" : "release"; - String abi = abis[abi_index]; - String filename = p_so.path.get_file(); - String dst_path = base.plus_file(type).plus_file(abi).plus_file(filename); - Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path); - print_verbose("Copying .so file from " + p_so.path + " to " + dst_path); - Error err = store_file_at_path(dst_path, data); - ERR_FAIL_COND_V_MSG(err, err, "Failed to copy .so file from " + p_so.path + " to " + dst_path); - export_data->libs.push_back(dst_path); - } - } - ERR_FAIL_COND_V_MSG(!exported, FAILED, - "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + String(" ").join(abis)); - return OK; - } - - void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { - const char **aperms = android_perms; - while (*aperms) { - bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); - if (enabled) { - r_permissions.push_back("android.permission." + String(*aperms)); - } - aperms++; - } - PackedStringArray user_perms = p_preset->get("permissions/custom_permissions"); - for (int i = 0; i < user_perms.size(); i++) { - String user_perm = user_perms[i].strip_edges(); - if (!user_perm.is_empty()) { - r_permissions.push_back(user_perm); - } - } - if (p_give_internet) { - if (r_permissions.find("android.permission.INTERNET") == -1) { - r_permissions.push_back("android.permission.INTERNET"); - } - } - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1 /* XRMode.OVR */) { - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > 0) { - if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { - r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); - } - } - } - } - - void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { - print_verbose("Building temporary manifest.."); - String manifest_text = - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" - "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" - " xmlns:tools=\"http://schemas.android.com/tools\">\n"; - - manifest_text += _get_screen_sizes_tag(p_preset); - manifest_text += _get_gles_tag(); - - Vector<String> perms; - _get_permissions(p_preset, p_give_internet, perms); - for (int i = 0; i < perms.size(); i++) { - manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i)); - } - - manifest_text += _get_xr_features_tag(p_preset); - manifest_text += _get_instrumentation_tag(p_preset); - manifest_text += _get_application_tag(p_preset); - manifest_text += "</manifest>\n"; - String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); - - print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text); - store_string_at_path(manifest_path, manifest_text); - } - - void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { - // Leaving the unused types commented because looking these constants up - // again later would be annoying - // const int CHUNK_AXML_FILE = 0x00080003; - // const int CHUNK_RESOURCEIDS = 0x00080180; - const int CHUNK_STRINGS = 0x001C0001; - // const int CHUNK_XML_END_NAMESPACE = 0x00100101; - const int CHUNK_XML_END_TAG = 0x00100103; - // const int CHUNK_XML_START_NAMESPACE = 0x00100100; - const int CHUNK_XML_START_TAG = 0x00100102; - // const int CHUNK_XML_TEXT = 0x00100104; - const int UTF8_FLAG = 0x00000100; - - Vector<String> string_table; - - uint32_t ofs = 8; - - uint32_t string_count = 0; - //uint32_t styles_count = 0; - uint32_t string_flags = 0; - uint32_t string_data_offset = 0; - - //uint32_t styles_offset = 0; - uint32_t string_table_begins = 0; - uint32_t string_table_ends = 0; - Vector<uint8_t> stable_extra; - - String version_name = p_preset->get("version/name"); - int version_code = p_preset->get("version/code"); - String package_name = p_preset->get("package/unique_name"); - - const int screen_orientation = - _get_android_orientation_value(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); - - bool screen_support_small = p_preset->get("screen/support_small"); - bool screen_support_normal = p_preset->get("screen/support_normal"); - bool screen_support_large = p_preset->get("screen/support_large"); - bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - - bool backup_allowed = p_preset->get("user_data_backup/allow"); - bool classify_as_game = p_preset->get("package/classify_as_game"); - - Vector<String> perms; - // Write permissions into the perms variable. - _get_permissions(p_preset, p_give_internet, perms); - - while (ofs < (uint32_t)p_manifest.size()) { - uint32_t chunk = decode_uint32(&p_manifest[ofs]); - uint32_t size = decode_uint32(&p_manifest[ofs + 4]); - - switch (chunk) { - case CHUNK_STRINGS: { - int iofs = ofs + 8; - - string_count = decode_uint32(&p_manifest[iofs]); - //styles_count = decode_uint32(&p_manifest[iofs + 4]); - string_flags = decode_uint32(&p_manifest[iofs + 8]); - string_data_offset = decode_uint32(&p_manifest[iofs + 12]); - //styles_offset = decode_uint32(&p_manifest[iofs + 16]); - /* - printf("string count: %i\n",string_count); - printf("flags: %i\n",string_flags); - printf("sdata ofs: %i\n",string_data_offset); - printf("styles ofs: %i\n",styles_offset); - */ - uint32_t st_offset = iofs + 20; - string_table.resize(string_count); - uint32_t string_end = 0; - - string_table_begins = st_offset; - - for (uint32_t i = 0; i < string_count; i++) { - uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]); - string_at += st_offset + string_count * 4; - - ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table."); - - if (string_flags & UTF8_FLAG) { - } else { - uint32_t len = decode_uint16(&p_manifest[string_at]); - Vector<char32_t> ucstring; - ucstring.resize(len + 1); - for (uint32_t j = 0; j < len; j++) { - uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); - ucstring.write[j] = c; - } - string_end = MAX(string_at + 2 + 2 * len, string_end); - ucstring.write[len] = 0; - string_table.write[i] = ucstring.ptr(); - } - } - - for (uint32_t i = string_end; i < (ofs + size); i++) { - stable_extra.push_back(p_manifest[i]); - } - - string_table_ends = ofs + size; - - } break; - case CHUNK_XML_START_TAG: { - int iofs = ofs + 8; - uint32_t name = decode_uint32(&p_manifest[iofs + 12]); - - String tname = string_table[name]; - uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]); - iofs += 28; - - for (uint32_t i = 0; i < attrcount; i++) { - uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]); - uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]); - uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]); - uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]); - - const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid); - String attrname = string_table[attr_name]; - const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : ""; - - //replace project information - if (tname == "manifest" && attrname == "package") { - string_table.write[attr_value] = get_package_name(package_name); - } - - if (tname == "manifest" && attrname == "versionCode") { - encode_uint32(version_code, &p_manifest.write[iofs + 16]); - } - - if (tname == "manifest" && attrname == "versionName") { - if (attr_value == 0xFFFFFFFF) { - WARN_PRINT("Version name in a resource, should be plain text"); - } else { - string_table.write[attr_value] = version_name; - } - } - - if (tname == "application" && attrname == "allowBackup") { - encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); - } - - if (tname == "application" && attrname == "isGame") { - encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); - } - - if (tname == "instrumentation" && attrname == "targetPackage") { - string_table.write[attr_value] = get_package_name(package_name); - } - - if (tname == "activity" && attrname == "screenOrientation") { - encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]); - } - - if (tname == "supports-screens") { - if (attrname == "smallScreens") { - encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - - } else if (attrname == "normalScreens") { - encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - - } else if (attrname == "largeScreens") { - encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - - } else if (attrname == "xlargeScreens") { - encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - } - } - - iofs += 20; - } - - } break; - case CHUNK_XML_END_TAG: { - int iofs = ofs + 8; - uint32_t name = decode_uint32(&p_manifest[iofs + 12]); - String tname = string_table[name]; - - if (tname == "uses-feature") { - Vector<String> feature_names; - Vector<bool> feature_required_list; - Vector<int> feature_versions; - - if (xr_mode_index == 1 /* XRMode.OVR */) { - // Check for hand tracking - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > 0) { - feature_names.push_back("oculus.software.handtracking"); - feature_required_list.push_back(hand_tracking_index == 2); - feature_versions.push_back(-1); // no version attribute should be added. - } - } - - if (feature_names.size() > 0) { - ofs += 24; // skip over end tag - - // save manifest ending so we can restore it - Vector<uint8_t> manifest_end; - uint32_t manifest_cur_size = p_manifest.size(); - - manifest_end.resize(p_manifest.size() - ofs); - memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); - - int32_t attr_name_string = string_table.find("name"); - ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); - - int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); - if (ns_android_string == -1) { - string_table.push_back("http://schemas.android.com/apk/res/android"); - ns_android_string = string_table.size() - 1; - } - - int32_t attr_uses_feature_string = string_table.find("uses-feature"); - if (attr_uses_feature_string == -1) { - string_table.push_back("uses-feature"); - attr_uses_feature_string = string_table.size() - 1; - } - - int32_t attr_required_string = string_table.find("required"); - if (attr_required_string == -1) { - string_table.push_back("required"); - attr_required_string = string_table.size() - 1; - } - - for (int i = 0; i < feature_names.size(); i++) { - String feature_name = feature_names[i]; - bool feature_required = feature_required_list[i]; - int feature_version = feature_versions[i]; - bool has_version_attribute = feature_version != -1; - - print_line("Adding feature " + feature_name); - - int32_t feature_string = string_table.find(feature_name); - if (feature_string == -1) { - string_table.push_back(feature_name); - feature_string = string_table.size() - 1; - } - - String required_value_string = feature_required ? "true" : "false"; - int32_t required_value = string_table.find(required_value_string); - if (required_value == -1) { - string_table.push_back(required_value_string); - required_value = string_table.size() - 1; - } - - int32_t attr_version_string = -1; - int32_t version_value = -1; - int tag_size; - int attr_count; - if (has_version_attribute) { - attr_version_string = string_table.find("version"); - if (attr_version_string == -1) { - string_table.push_back("version"); - attr_version_string = string_table.size() - 1; - } - - version_value = string_table.find(itos(feature_version)); - if (version_value == -1) { - string_table.push_back(itos(feature_version)); - version_value = string_table.size() - 1; - } - - tag_size = 96; // node and three attrs + end node - attr_count = 3; - } else { - tag_size = 76; // node and two attrs + end node - attr_count = 2; - } - manifest_cur_size += tag_size + 24; - p_manifest.resize(manifest_cur_size); - - // start tag - encode_uint16(0x102, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name - encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start - encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size - encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs - encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index - encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index - encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index - - // android:name attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns - encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' - encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size - p_manifest.write[ofs + 50] = 0; // typedvalue_always0 - p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) - encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference - - // android:required attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns - encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' - encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size - p_manifest.write[ofs + 70] = 0; // typedvalue_always0 - p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) - encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference - - ofs += 76; - - if (has_version_attribute) { - // android:version attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns - encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name' - encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size - p_manifest.write[ofs + 14] = 0; // typedvalue_always0 - p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string) - encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference - - ofs += 20; - } - - // end tag - encode_uint16(0x103, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(24, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name - - ofs += 24; - } - memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); - ofs -= 24; // go back over back end - } - } - if (tname == "manifest") { - // save manifest ending so we can restore it - Vector<uint8_t> manifest_end; - uint32_t manifest_cur_size = p_manifest.size(); - - manifest_end.resize(p_manifest.size() - ofs); - memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); - - int32_t attr_name_string = string_table.find("name"); - ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); - - int32_t ns_android_string = string_table.find("android"); - ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace."); - - int32_t attr_uses_permission_string = string_table.find("uses-permission"); - if (attr_uses_permission_string == -1) { - string_table.push_back("uses-permission"); - attr_uses_permission_string = string_table.size() - 1; - } - - for (int i = 0; i < perms.size(); ++i) { - print_line("Adding permission " + perms[i]); - - manifest_cur_size += 56 + 24; // node + end node - p_manifest.resize(manifest_cur_size); - - // Add permission to the string pool - int32_t perm_string = string_table.find(perms[i]); - if (perm_string == -1) { - string_table.push_back(perms[i]); - perm_string = string_table.size() - 1; - } - - // start tag - encode_uint16(0x102, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(56, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name - encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start - encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size - encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs - encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index - encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index - encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index - - // attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns - encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' - encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size - p_manifest.write[ofs + 50] = 0; // typedvalue_always0 - p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) - encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference - - ofs += 56; - - // end tag - encode_uint16(0x103, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(24, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name - - ofs += 24; - } - - // copy footer back in - memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); - } - } break; - } - - ofs += size; - } - - //create new andriodmanifest binary - - Vector<uint8_t> ret; - ret.resize(string_table_begins + string_table.size() * 4); - - for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = p_manifest[i]; - } - - ofs = 0; - for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); - ofs += string_table[i].length() * 2 + 2 + 2; - } - - ret.resize(ret.size() + ofs); - string_data_offset = ret.size() - ofs; - uint8_t *chars = &ret.write[string_data_offset]; - for (int i = 0; i < string_table.size(); i++) { - String s = string_table[i]; - encode_uint16(s.length(), chars); - chars += 2; - for (int j = 0; j < s.length(); j++) { - encode_uint16(s[j], chars); - chars += 2; - } - encode_uint16(0, chars); - chars += 2; - } - - for (int i = 0; i < stable_extra.size(); i++) { - ret.push_back(stable_extra[i]); - } - - //pad - while (ret.size() % 4) { - ret.push_back(0); - } - - uint32_t new_stable_end = ret.size(); - - uint32_t extra = (p_manifest.size() - string_table_ends); - ret.resize(new_stable_end + extra); - for (uint32_t i = 0; i < extra; i++) { - ret.write[new_stable_end + i] = p_manifest[string_table_ends + i]; - } - - while (ret.size() % 4) { - ret.push_back(0); - } - encode_uint32(ret.size(), &ret.write[4]); //update new file size - - encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size - encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings - encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset - - p_manifest = ret; - } - - static String _parse_string(const uint8_t *p_bytes, bool p_utf8) { - uint32_t offset = 0; - uint32_t len = 0; - - if (p_utf8) { - uint8_t byte = p_bytes[offset]; - if (byte & 0x80) { - offset += 2; - } else { - offset += 1; - } - byte = p_bytes[offset]; - offset++; - if (byte & 0x80) { - len = byte & 0x7F; - len = (len << 8) + p_bytes[offset]; - offset++; - } else { - len = byte; - } - } else { - len = decode_uint16(&p_bytes[offset]); - offset += 2; - if (len & 0x8000) { - len &= 0x7FFF; - len = (len << 16) + decode_uint16(&p_bytes[offset]); - offset += 2; - } - } - - if (p_utf8) { - Vector<uint8_t> str8; - str8.resize(len + 1); - for (uint32_t i = 0; i < len; i++) { - str8.write[i] = p_bytes[offset + i]; - } - str8.write[len] = 0; - String str; - str.parse_utf8((const char *)str8.ptr()); - return str; - } else { - String str; - for (uint32_t i = 0; i < len; i++) { - char32_t c = decode_uint16(&p_bytes[offset + i * 2]); - if (c == 0) { - break; - } - str += String::chr(c); - } - return str; - } - } - - void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) { - const int UTF8_FLAG = 0x00000100; - - uint32_t string_block_len = decode_uint32(&r_manifest[16]); - uint32_t string_count = decode_uint32(&r_manifest[20]); - uint32_t string_flags = decode_uint32(&r_manifest[28]); - const uint32_t string_table_begins = 40; - - Vector<String> string_table; - - String package_name = p_preset->get("package/name"); - - for (uint32_t i = 0; i < string_count; i++) { - uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]); - offset += string_table_begins + string_count * 4; - - String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG); - - if (str.begins_with("godot-project-name")) { - if (str == "godot-project-name") { - //project name - str = get_project_name(package_name); - - } else { - String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_"); - String prop = "application/config/name_" + lang; - if (ProjectSettings::get_singleton()->has_setting(prop)) { - str = ProjectSettings::get_singleton()->get(prop); - } else { - str = get_project_name(package_name); - } - } - } - - string_table.push_back(str); - } - - //write a new string table, but use 16 bits - Vector<uint8_t> ret; - ret.resize(string_table_begins + string_table.size() * 4); - - for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = r_manifest[i]; - } - - int ofs = 0; - for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); - ofs += string_table[i].length() * 2 + 2 + 2; - } - - ret.resize(ret.size() + ofs); - uint8_t *chars = &ret.write[ret.size() - ofs]; - for (int i = 0; i < string_table.size(); i++) { - String s = string_table[i]; - encode_uint16(s.length(), chars); - chars += 2; - for (int j = 0; j < s.length(); j++) { - encode_uint16(s[j], chars); - chars += 2; - } - encode_uint16(0, chars); - chars += 2; - } - - //pad - while (ret.size() % 4) { - ret.push_back(0); - } - - //change flags to not use utf8 - encode_uint32(string_flags & ~0x100, &ret.write[28]); - //change length - encode_uint32(ret.size() - 12, &ret.write[16]); - //append the rest... - int rest_from = 12 + string_block_len; - int rest_to = ret.size(); - int rest_len = (r_manifest.size() - rest_from); - ret.resize(ret.size() + (r_manifest.size() - rest_from)); - for (int i = 0; i < rest_len; i++) { - ret.write[rest_to + i] = r_manifest[rest_from + i]; - } - //finally update the size - encode_uint32(ret.size(), &ret.write[4]); - - r_manifest = ret; - //printf("end\n"); - } - - void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) { - Vector<uint8_t> png_buffer; - Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer); - if (err == OK) { - p_data.resize(png_buffer.size()); - memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); - } else { - String err_str = String("Failed to convert splash image to png."); - WARN_PRINT(err_str.utf8().get_data()); - } - } - - void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) { - Ref<Image> working_image = p_source_image; - - if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) { - working_image = p_source_image->duplicate(); - working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS); - } - - Vector<uint8_t> png_buffer; - Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); - if (err == OK) { - p_data.resize(png_buffer.size()); - memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); - } else { - String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png."; - WARN_PRINT(err_str.utf8().get_data()); - } - } - - String load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) { - bool scale_splash = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - bool apply_filter = ProjectSettings::get_singleton()->get("application/boot_splash/use_filter"); - String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - - if (!project_splash_path.is_empty()) { - splash_image.instantiate(); - print_verbose("Loading splash image: " + project_splash_path); - const Error err = ImageLoader::load_image(project_splash_path, splash_image); - if (err) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")"); - } - splash_image.unref(); - } - } - - if (splash_image.is_null()) { - // Use the default - print_verbose("Using default splash image."); - splash_image = Ref<Image>(memnew(Image(boot_splash_png))); - } - - if (scale_splash) { - Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); - int width, height; - if (screen_size.width > screen_size.height) { - // scale horizontally - height = screen_size.height; - width = splash_image->get_width() * screen_size.height / splash_image->get_height(); - } else { - // scale vertically - width = screen_size.width; - height = splash_image->get_height() * screen_size.width / splash_image->get_width(); - } - splash_image->resize(width, height); - } - - // Setup the splash bg color - bool bg_color_valid; - Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); - if (!bg_color_valid) { - bg_color = boot_splash_bg_color; - } - - print_verbose("Creating splash background color image."); - splash_bg_color_image.instantiate(); - splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); - splash_bg_color_image->fill(bg_color); - - String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); - return processed_splash_config_xml; - } - - void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { - String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); - - icon.instantiate(); - foreground.instantiate(); - background.instantiate(); - - // Regular icon: user selection -> project icon -> default. - String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); - print_verbose("Loading regular icon from " + path); - if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) { - print_verbose("- falling back to project icon: " + project_icon_path); - ImageLoader::load_image(project_icon_path, icon); - } - - // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). - path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); - print_verbose("Loading adaptive foreground icon from " + path); - if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) { - print_verbose("- falling back to using the regular icon"); - foreground = icon; - } - - // Adaptive background: user selection -> default. - path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); - if (!path.is_empty()) { - print_verbose("Loading adaptive background icon from " + path); - ImageLoader::load_image(path, background); - } - } - - void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) { - store_image(launcher_icon.export_path, data); - } - - void store_image(const String &export_path, const Vector<uint8_t> &data) { - String img_path = export_path.insert(0, "res://android/build/"); - store_file_at_path(img_path, data); - } - - void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, - const String &processed_splash_config_xml, - const Ref<Image> &splash_image, - const Ref<Image> &splash_bg_color_image, - const Ref<Image> &main_image, - const Ref<Image> &foreground, - const Ref<Image> &background) { - // Store the splash configuration - if (!processed_splash_config_xml.is_empty()) { - print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml); - store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml); - } - - // Store the splash image - if (splash_image.is_valid() && !splash_image->is_empty()) { - print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH)); - Vector<uint8_t> data; - _load_image_data(splash_image, data); - store_image(SPLASH_IMAGE_EXPORT_PATH, data); - } - - // Store the splash bg color image - if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { - print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH)); - Vector<uint8_t> data; - _load_image_data(splash_bg_color_image, data); - store_image(SPLASH_BG_COLOR_PATH, data); - } - - // Prepare images to be resized for the icons. If some image ends up being uninitialized, - // the default image from the export template will be used. - - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path); - Vector<uint8_t> data; - _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data); - store_image(launcher_icons[i], data); - } - - if (foreground.is_valid() && !foreground->is_empty()) { - print_verbose("Processing launcher adaptive icon foreground for dimension " + itos(launcher_adaptive_icon_foregrounds[i].dimensions) + " into " + launcher_adaptive_icon_foregrounds[i].export_path); - Vector<uint8_t> data; - _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground, - launcher_adaptive_icon_foregrounds[i].dimensions, data); - store_image(launcher_adaptive_icon_foregrounds[i], data); - } - - if (background.is_valid() && !background->is_empty()) { - print_verbose("Processing launcher adaptive icon background for dimension " + itos(launcher_adaptive_icon_backgrounds[i].dimensions) + " into " + launcher_adaptive_icon_backgrounds[i].export_path); - Vector<uint8_t> data; - _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background, - launcher_adaptive_icon_backgrounds[i].dimensions, data); - store_image(launcher_adaptive_icon_backgrounds[i], data); - } - } - } - - static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { - Vector<String> abis = get_abis(); - Vector<String> enabled_abis; - for (int i = 0; i < abis.size(); ++i) { - bool is_enabled = p_preset->get("architectures/" + abis[i]); - if (is_enabled) { - enabled_abis.push_back(abis[i]); - } - } - return enabled_abis; - } - -public: - typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); - -public: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - if (driver == "GLES2") { - r_features->push_back("etc"); - } - // FIXME: Review what texture formats are used for Vulkan. - if (driver == "Vulkan") { - r_features->push_back("etc2"); - } - - Vector<String> abis = get_enabled_abis(p_preset); - for (int i = 0; i < abis.size(); ++i) { - r_features->push_back(abis[i]); - } - } - - virtual void get_export_options(List<ExportOption> *r_options) override { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK)); - - Vector<PluginConfigAndroid> plugins_configs = get_plugins(); - for (int i = 0; i < plugins_configs.size(); i++) { - print_verbose("Found Android plugin " + plugins_configs[i].name); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false)); - } - plugins_changed.clear(); - - Vector<String> abis = get_abis(); - for (int i = 0; i < abis.size(); ++i) { - String abi = abis[i]; - bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a"); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); - } - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray())); - - const char **perms = android_perms; - while (*perms) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false)); - perms++; - } - } - - virtual String get_name() const override { - return "Android"; - } - - virtual String get_os_name() const override { - return "Android"; - } - - virtual Ref<Texture2D> get_logo() const override { - return logo; - } - - virtual bool should_update_export_options() override { - bool export_options_changed = plugins_changed.is_set(); - if (export_options_changed) { - // don't clear unless we're reporting true, to avoid race - plugins_changed.clear(); - } - return export_options_changed; - } - - virtual bool poll_export() override { - bool dc = devices_changed.is_set(); - if (dc) { - // don't clear unless we're reporting true, to avoid race - devices_changed.clear(); - } - return dc; - } - - virtual int get_options_count() const override { - MutexLock lock(device_lock); - return devices.size(); - } - - virtual String get_options_tooltip() const override { - return TTR("Select device from the list"); - } - - virtual String get_option_label(int p_index) const override { - ERR_FAIL_INDEX_V(p_index, devices.size(), ""); - MutexLock lock(device_lock); - return devices[p_index].name; - } - - virtual String get_option_tooltip(int p_index) const override { - ERR_FAIL_INDEX_V(p_index, devices.size(), ""); - MutexLock lock(device_lock); - String s = devices[p_index].description; - if (devices.size() == 1) { - // Tooltip will be: - // Name - // Description - s = devices[p_index].name + "\n\n" + s; - } - return s; - } - - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override { - ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); - - String can_export_error; - bool can_export_missing_templates; - if (!can_export(p_preset, can_export_error, can_export_missing_templates)) { - EditorNode::add_io_error(can_export_error); - return ERR_UNCONFIGURED; - } - - MutexLock lock(device_lock); - - EditorProgress ep("run", vformat(TTR("Running on %s"), devices[p_device].name), 3); - - String adb = get_adb_path(); - - // Export_temp APK. - if (ep.step(TTR("Exporting APK..."), 0)) { - return ERR_SKIP; - } - - const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); - const bool use_reverse = devices[p_device].api_level >= 21; - - if (use_reverse) { - p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; - } - - String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); - -#define CLEANUP_AND_RETURN(m_err) \ - { \ - DirAccess::remove_file_or_error(tmp_export_path); \ - return m_err; \ - } - - // Export to temporary APK before sending to device. - Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags); - - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - - List<String> args; - int rv; - String output; - - bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install"); - String version_name = p_preset->get("version/name"); - String package_name = p_preset->get("package/unique_name"); - - if (remove_prev) { - if (ep.step(TTR("Uninstalling..."), 1)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - print_line("Uninstalling previous version: " + devices[p_device].name); - - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("uninstall"); - args.push_back(get_package_name(package_name)); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - } - - print_line("Installing to device (please wait...): " + devices[p_device].name); - if (ep.step(TTR("Installing to device, please wait..."), 2)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("install"); - args.push_back("-r"); - args.push_back(tmp_export_path); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - if (err || rv != 0) { - EditorNode::add_io_error(vformat(TTR("Could not install to device: %s"), output)); - CLEANUP_AND_RETURN(ERR_CANT_CREATE); - } - - if (use_remote) { - if (use_reverse) { - static const char *const msg = "--- Device API >= 21; debugging over USB ---"; - EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); - print_line(String(msg).to_upper()); - - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("--remove-all"); - output.clear(); - OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - - if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { - int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("tcp:" + itos(dbg_port)); - args.push_back("tcp:" + itos(dbg_port)); - - output.clear(); - OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - print_line("Reverse result: " + itos(rv)); - } - - if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { - int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); - - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("tcp:" + itos(fs_port)); - args.push_back("tcp:" + itos(fs_port)); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - print_line("Reverse result2: " + itos(rv)); - } - } else { - static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; - EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); - print_line(String(msg).to_upper()); - } - } - - if (ep.step(TTR("Running on device..."), 3)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("shell"); - args.push_back("am"); - args.push_back("start"); - if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 - args.push_back("--user"); - args.push_back("0"); - } - args.push_back("-a"); - args.push_back("android.intent.action.MAIN"); - args.push_back("-n"); - args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp"); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - if (err || rv != 0) { - EditorNode::add_io_error(TTR("Could not execute on device.")); - CLEANUP_AND_RETURN(ERR_CANT_CREATE); - } - - CLEANUP_AND_RETURN(OK); -#undef CLEANUP_AND_RETURN - } - - virtual Ref<Texture2D> get_run_icon() const override { - return run_icon; - } - - static String get_adb_path() { - String exe_ext = ""; - if (OS::get_singleton()->get_name() == "Windows") { - exe_ext = ".exe"; - } - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - return sdk_path.plus_file("platform-tools/adb" + exe_ext); - } - - static String get_apksigner_path() { - String exe_ext = ""; - if (OS::get_singleton()->get_name() == "Windows") { - exe_ext = ".bat"; - } - String apksigner_command_name = "apksigner" + exe_ext; - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - String apksigner_path = ""; - - Error errn; - String build_tools_dir = sdk_path.plus_file("build-tools"); - DirAccessRef da = DirAccess::open(build_tools_dir, &errn); - if (errn != OK) { - print_error("Unable to open Android 'build-tools' directory."); - return apksigner_path; - } - - // There are additional versions directories we need to go through. - da->list_dir_begin(); - String sub_dir = da->get_next(); - while (!sub_dir.is_empty()) { - if (!sub_dir.begins_with(".") && da->current_is_dir()) { - // Check if the tool is here. - String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name); - if (FileAccess::exists(tool_path)) { - apksigner_path = tool_path; - break; - } - } - sub_dir = da->get_next(); - } - da->list_dir_end(); - - if (apksigner_path.is_empty()) { - EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool.")); - } - - return apksigner_path; - } - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - if (!bool(p_preset->get("custom_template/use_custom_build"))) { - String template_err; - bool dvalid = false; - bool rvalid = false; - bool has_export_templates = false; - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - template_err += TTR("Custom debug template not found.") + "\n"; - } - } else { - has_export_templates |= exists_export_template("android_debug.apk", &template_err); - } - - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - template_err += TTR("Custom release template not found.") + "\n"; - } - } else { - has_export_templates |= exists_export_template("android_release.apk", &template_err); - } - - r_missing_templates = !has_export_templates; - valid = dvalid || rvalid || has_export_templates; - if (!valid) { - err += template_err; - } - } else { - bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle"); - if (!installed_android_build_template) { - r_missing_templates = !exists_export_template("android_source.zip", &err); - err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; - } else { - r_missing_templates = false; - } - - valid = installed_android_build_template && !r_missing_templates; - } - - // Validate the rest of the configuration. - - String dk = p_preset->get("keystore/debug"); - String dk_user = p_preset->get("keystore/debug_user"); - String dk_password = p_preset->get("keystore/debug_password"); - - if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) { - valid = false; - err += TTR("Either Debug Keystore, Debug User AND Debug Password settings must be configured OR none of them.") + "\n"; - } - - if (!FileAccess::exists(dk)) { - dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - if (!FileAccess::exists(dk)) { - valid = false; - err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n"; - } - } - - String rk = p_preset->get("keystore/release"); - String rk_user = p_preset->get("keystore/release_user"); - String rk_password = p_preset->get("keystore/release_password"); - - if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) { - valid = false; - err += TTR("Either Release Keystore, Release User AND Release Password settings must be configured OR none of them.") + "\n"; - } - - if (!rk.is_empty() && !FileAccess::exists(rk)) { - valid = false; - err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; - } - - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - if (sdk_path == "") { - err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; - valid = false; - } else { - Error errn; - // Check for the platform-tools directory. - DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); - if (errn != OK) { - err += TTR("Invalid Android SDK path in Editor Settings."); - err += TTR("Missing 'platform-tools' directory!"); - err += "\n"; - valid = false; - } - - // Validate that adb is available - String adb_path = get_adb_path(); - if (!FileAccess::exists(adb_path)) { - err += TTR("Unable to find Android SDK platform-tools' adb command."); - err += TTR("Please check in the Android SDK directory specified in Editor Settings."); - err += "\n"; - valid = false; - } - - // Check for the build-tools directory. - DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn); - if (errn != OK) { - err += TTR("Invalid Android SDK path in Editor Settings."); - err += TTR("Missing 'build-tools' directory!"); - err += "\n"; - valid = false; - } - - // Validate that apksigner is available - String apksigner_path = get_apksigner_path(); - if (!FileAccess::exists(apksigner_path)) { - err += TTR("Unable to find Android SDK build-tools' apksigner command."); - err += TTR("Please check in the Android SDK directory specified in Editor Settings."); - err += "\n"; - valid = false; - } - } - - bool apk_expansion = p_preset->get("apk_expansion/enable"); - - if (apk_expansion) { - String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - - if (apk_expansion_pkey == "") { - valid = false; - - err += TTR("Invalid public key for APK expansion.") + "\n"; - } - } - - String pn = p_preset->get("package/unique_name"); - String pn_err; - - if (!is_package_name_valid(get_package_name(pn), &pn_err)) { - valid = false; - err += TTR("Invalid package name:") + " " + pn_err + "\n"; - } - - String etc_error = test_etc2(); - if (etc_error != String()) { - valid = false; - err += etc_error; - } - - // Ensure that `Use Custom Build` is enabled if a plugin is selected. - String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); - bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); - if (!enabled_plugins_names.is_empty() && !custom_build_enabled) { - valid = false; - err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); - err += "\n"; - } - - // Validate the Xr features are properly populated - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - int hand_tracking = p_preset->get("xr_features/hand_tracking"); - if (xr_mode_index != /* XRMode.OVR*/ 1) { - if (hand_tracking > 0) { - valid = false; - err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); - err += "\n"; - } - } - - if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB && - !bool(p_preset->get("custom_template/use_custom_build"))) { - valid = false; - err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); - err += "\n"; - } - - r_error = err; - return valid; - } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - list.push_back("apk"); - list.push_back("aab"); - return list; - } - - inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) { - String plugin_names = get_plugins_names(enabled_plugins); - bool first_build = last_custom_build_time == 0; - bool have_plugins_changed = false; - - if (!first_build) { - have_plugins_changed = plugin_names != last_plugin_names; - if (!have_plugins_changed) { - for (int i = 0; i < enabled_plugins.size(); i++) { - if (enabled_plugins.get(i).last_updated > last_custom_build_time) { - have_plugins_changed = true; - break; - } - } - } - } - - last_custom_build_time = OS::get_singleton()->get_unix_time(); - last_plugin_names = plugin_names; - - return have_plugins_changed || first_build; - } - - String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - int version_code = p_preset->get("version/code"); - String package_name = p_preset->get("package/unique_name"); - String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; - String fullpath = p_path.get_base_dir().plus_file(apk_file_name); - return fullpath; - } - - Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - String fullpath = get_apk_expansion_fullpath(p_preset, p_path); - Error err = save_pack(p_preset, fullpath); - return err; - } - - void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) { - String cmdline = p_preset->get("command_line/extra_args"); - Vector<String> command_line_strings = cmdline.strip_edges().split(" "); - for (int i = 0; i < command_line_strings.size(); i++) { - if (command_line_strings[i].strip_edges().length() == 0) { - command_line_strings.remove(i); - i--; - } - } - - gen_export_flags(command_line_strings, p_flags); - - bool apk_expansion = p_preset->get("apk_expansion/enable"); - if (apk_expansion) { - String fullpath = get_apk_expansion_fullpath(p_preset, p_path); - String apk_expansion_public_key = p_preset->get("apk_expansion/public_key"); - - command_line_strings.push_back("--use_apk_expansion"); - command_line_strings.push_back("--apk_expansion_md5"); - command_line_strings.push_back(FileAccess::get_md5(fullpath)); - command_line_strings.push_back("--apk_expansion_key"); - command_line_strings.push_back(apk_expansion_public_key.strip_edges()); - } - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1) { - command_line_strings.push_back("--xr_mode_ovr"); - } else { // XRMode.REGULAR is the default. - command_line_strings.push_back("--xr_mode_regular"); - } - - bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer"); - if (use_32_bit_framebuffer) { - command_line_strings.push_back("--use_depth_32"); - } - - bool immersive = p_preset->get("screen/immersive_mode"); - if (immersive) { - command_line_strings.push_back("--use_immersive"); - } - - bool debug_opengl = p_preset->get("graphics/opengl_debug"); - if (debug_opengl) { - command_line_strings.push_back("--debug_opengl"); - } - - if (command_line_strings.size()) { - r_command_line_flags.resize(4); - encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]); - for (int i = 0; i < command_line_strings.size(); i++) { - print_line(itos(i) + " param: " + command_line_strings[i]); - CharString command_line_argument = command_line_strings[i].utf8(); - int base = r_command_line_flags.size(); - int length = command_line_argument.length(); - if (length == 0) { - continue; - } - r_command_line_flags.resize(base + 4 + length); - encode_uint32(length, &r_command_line_flags.write[base]); - memcpy(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length); - } - } - } - - Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { - int export_format = int(p_preset->get("custom_template/export_format")); - String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; - String release_keystore = p_preset->get("keystore/release"); - String release_username = p_preset->get("keystore/release_user"); - String release_password = p_preset->get("keystore/release_password"); - - String apksigner = get_apksigner_path(); - print_verbose("Starting signing of the " + export_label + " binary using " + apksigner); - if (!FileAccess::exists(apksigner)) { - EditorNode::add_io_error(vformat(TTR("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting %s is unsigned."), export_label)); - return OK; - } - - String keystore; - String password; - String user; - if (p_debug) { - keystore = p_preset->get("keystore/debug"); - password = p_preset->get("keystore/debug_password"); - user = p_preset->get("keystore/debug_user"); - - if (keystore.is_empty()) { - keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); - } - - if (ep.step(vformat(TTR("Signing debug %s..."), export_label), 104)) { - return ERR_SKIP; - } - - } else { - keystore = release_keystore; - password = release_password; - user = release_username; - - if (ep.step(vformat(TTR("Signing release %s..."), export_label), 104)) { - return ERR_SKIP; - } - } - - if (!FileAccess::exists(keystore)) { - EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); - return ERR_FILE_CANT_OPEN; - } - - String output; - List<String> args; - args.push_back("sign"); - args.push_back("--verbose"); - args.push_back("--ks"); - args.push_back(keystore); - args.push_back("--ks-pass"); - args.push_back("pass:" + password); - args.push_back("--ks-key-alias"); - args.push_back(user); - args.push_back(export_path); - if (p_debug) { - // We only print verbose logs for debug builds to avoid leaking release keystore credentials. - print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); - } - int retval; - output.clear(); - OS::get_singleton()->execute(apksigner, args, &output, &retval, true); - print_verbose(output); - if (retval) { - EditorNode::add_io_error(vformat(TTR("'apksigner' returned with error #%d"), retval)); - return ERR_CANT_CREATE; - } - - if (ep.step(vformat(TTR("Verifying %s..."), export_label), 105)) { - return ERR_SKIP; - } - - args.clear(); - args.push_back("verify"); - args.push_back("--verbose"); - args.push_back(export_path); - if (p_debug) { - print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); - } - - output.clear(); - OS::get_singleton()->execute(apksigner, args, &output, &retval, true); - print_verbose(output); - if (retval) { - EditorNode::add_io_error(vformat(TTR("'apksigner' verification of %s failed."), export_label)); - return ERR_CANT_CREATE; - } - - print_verbose("Successfully completed signing build."); - return OK; - } - - void _clear_assets_directory() { - DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (da_res->dir_exists("res://android/build/assets")) { - print_verbose("Clearing assets directory.."); - DirAccessRef da_assets = DirAccess::open("res://android/build/assets"); - da_assets->erase_contents_recursive(); - da_res->remove("res://android/build/assets"); - } - } - - void _remove_copied_libs() { - print_verbose("Removing previously installed libraries..."); - Error error; - String libs_json = FileAccess::get_file_as_string(GDNATIVE_LIBS_PATH, &error); - if (error || libs_json.is_empty()) { - print_verbose("No previously installed libraries found"); - return; - } - - JSON json; - error = json.parse(libs_json); - ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message()); - - Vector<String> libs = json.get_data(); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - for (int i = 0; i < libs.size(); i++) { - print_verbose("Removing previously installed library " + libs[i]); - da->remove(libs[i]); - } - da->remove(GDNATIVE_LIBS_PATH); - } - - String join_list(List<String> parts, const String &separator) const { - String ret; - for (int i = 0; i < parts.size(); ++i) { - if (i > 0) { - ret += separator; - } - ret += parts[i]; - } - return ret; - } - - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override { - int export_format = int(p_preset->get("custom_template/export_format")); - bool should_sign = p_preset->get("package/signed"); - return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags); - } - - Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_apk; - Error err; - - EditorProgress ep("export", TTR("Exporting for Android"), 105, true); - - bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build")); - bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); - bool apk_expansion = p_preset->get("apk_expansion/enable"); - Vector<String> enabled_abis = get_enabled_abis(p_preset); - - print_verbose("Exporting for Android..."); - print_verbose("- debug build: " + bool_to_string(p_debug)); - print_verbose("- export path: " + p_path); - print_verbose("- export format: " + itos(export_format)); - print_verbose("- sign build: " + bool_to_string(should_sign)); - print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); - print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); - print_verbose("- enabled abis: " + String(",").join(enabled_abis)); - print_verbose("- export filter: " + itos(p_preset->get_export_filter())); - print_verbose("- include filter: " + p_preset->get_include_filter()); - print_verbose("- exclude filter: " + p_preset->get_exclude_filter()); - - Ref<Image> splash_image; - Ref<Image> splash_bg_color_image; - String processed_splash_config_xml = load_splash_refs(splash_image, splash_bg_color_image); - - Ref<Image> main_image; - Ref<Image> foreground; - Ref<Image> background; - - load_icon_refs(p_preset, main_image, foreground, background); - - Vector<uint8_t> command_line_flags; - // Write command line flags into the command_line_flags variable. - get_command_line_flags(p_preset, p_path, p_flags, command_line_flags); - - if (export_format == EXPORT_FORMAT_AAB) { - if (!p_path.ends_with(".aab")) { - EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension.")); - return ERR_UNCONFIGURED; - } - if (apk_expansion) { - EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle.")); - return ERR_UNCONFIGURED; - } - } - if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) { - EditorNode::get_singleton()->show_warning( - TTR("Invalid filename! Android APK requires the *.apk extension.")); - return ERR_UNCONFIGURED; - } - if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) { - EditorNode::add_io_error(TTR("Unsupported export format!\n")); - return ERR_UNCONFIGURED; //TODO: is this the right error? - } - - if (use_custom_build) { - print_verbose("Starting custom build.."); - //test that installed build version is alright - { - print_verbose("Checking build version.."); - FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); - return ERR_UNCONFIGURED; - } - String version = f->get_line().strip_edges(); - print_verbose("- build version: " + version); - f->close(); - if (version != VERSION_FULL_CONFIG) { - EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG)); - return ERR_UNCONFIGURED; - } - } - String sdk_path = EDITOR_GET("export/android/android_sdk_path"); - ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); - print_verbose("Android sdk path: " + sdk_path); - - // TODO: should we use "package/name" or "application/config/name"? - String project_name = get_project_name(p_preset->get("package/name")); - err = _create_project_name_strings_files(p_preset, project_name); //project name localization. - if (err != OK) { - EditorNode::add_io_error(TTR("Unable to overwrite res://android/build/res/*.xml files with project name")); - } - // Copies the project icon files into the appropriate Gradle project directory. - _copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background); - // Write an AndroidManifest.xml file into the Gradle project directory. - _write_tmp_manifest(p_preset, p_give_internet, p_debug); - - //stores all the project files inside the Gradle project directory. Also includes all ABIs - _clear_assets_directory(); - _remove_copied_libs(); - if (!apk_expansion) { - print_verbose("Exporting project files.."); - CustomExportData user_data; - user_data.debug = p_debug; - err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); - if (err != OK) { - EditorNode::add_io_error(TTR("Could not export project files to gradle project\n")); - return err; - } - if (user_data.libs.size() > 0) { - FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE); - JSON json; - fa->store_string(json.stringify(user_data.libs, "\t")); - fa->close(); - } - } else { - print_verbose("Saving apk expansion file.."); - err = save_apk_expansion_file(p_preset, p_path); - if (err != OK) { - EditorNode::add_io_error(TTR("Could not write expansion package file!")); - return err; - } - } - print_verbose("Storing command line flags.."); - store_file_at_path("res://android/build/assets/_cl_", command_line_flags); - - print_verbose("Updating ANDROID_HOME environment to " + sdk_path); - OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required - String build_command; - -#ifdef WINDOWS_ENABLED - build_command = "gradlew.bat"; -#else - build_command = "gradlew"; -#endif - - String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build"); - build_command = build_path.plus_file(build_command); - - String package_name = get_package_name(p_preset->get("package/unique_name")); - String version_code = itos(p_preset->get("version/code")); - String version_name = p_preset->get("version/name"); - String enabled_abi_string = String("|").join(enabled_abis); - String sign_flag = should_sign ? "true" : "false"; - String zipalign_flag = "true"; - - Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset); - String local_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins); - String remote_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins); - String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins); - bool clean_build_required = is_clean_build_required(enabled_plugins); - - List<String> cmdline; - if (clean_build_required) { - cmdline.push_back("clean"); - } - - String build_type = p_debug ? "Debug" : "Release"; - if (export_format == EXPORT_FORMAT_AAB) { - String bundle_build_command = vformat("bundle%s", build_type); - cmdline.push_back(bundle_build_command); - } else if (export_format == EXPORT_FORMAT_APK) { - String apk_build_command = vformat("assemble%s", build_type); - cmdline.push_back(apk_build_command); - } - - cmdline.push_back("-p"); // argument to specify the start directory. - cmdline.push_back(build_path); // start directory. - cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. - cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code. - cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name. - cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs. - cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. - cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. - cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. - cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. - cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. - cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG)); - - // NOTE: The release keystore is not included in the verbose logging - // to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting. - // Any non-sensitive additions to the command line arguments must be done above this section. - // Sensitive additions must be done below the logging statement. - print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" "))); - - if (should_sign) { - if (p_debug) { - String debug_keystore = p_preset->get("keystore/debug"); - String debug_password = p_preset->get("keystore/debug_password"); - String debug_user = p_preset->get("keystore/debug_user"); - - if (debug_keystore.is_empty()) { - debug_keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); - } - - cmdline.push_back("-Pdebug_keystore_file=" + debug_keystore); // argument to specify the debug keystore file. - cmdline.push_back("-Pdebug_keystore_alias=" + debug_user); // argument to specify the debug keystore alias. - cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password. - } else { - // Pass the release keystore info as well - String release_keystore = p_preset->get("keystore/release"); - String release_username = p_preset->get("keystore/release_user"); - String release_password = p_preset->get("keystore/release_password"); - if (!FileAccess::exists(release_keystore)) { - EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); - return ERR_FILE_CANT_OPEN; - } - - cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file. - cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias. - cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specify the release keystore password. - } - } - - int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline); - if (result != 0) { - EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation.")); - return ERR_CANT_CREATE; - } - - List<String> copy_args; - String copy_command; - if (export_format == EXPORT_FORMAT_AAB) { - copy_command = vformat("copyAndRename%sAab", build_type); - } else if (export_format == EXPORT_FORMAT_APK) { - copy_command = vformat("copyAndRename%sApk", build_type); - } - - copy_args.push_back(copy_command); - - copy_args.push_back("-p"); // argument to specify the start directory. - copy_args.push_back(build_path); // start directory. - - String export_filename = p_path.get_file(); - String export_path = p_path.get_base_dir(); - if (export_path.is_rel_path()) { - export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); - } - export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); - - copy_args.push_back("-Pexport_path=file:" + export_path); - copy_args.push_back("-Pexport_filename=" + export_filename); - - print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" "))); - int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args); - if (copy_result != 0) { - EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs.")); - return ERR_CANT_CREATE; - } - - print_verbose("Successfully completed Android custom build."); - return OK; - } - // This is the start of the Legacy build system - print_verbose("Starting legacy build system.."); - if (p_debug) { - src_apk = p_preset->get("custom_template/debug"); - } else { - src_apk = p_preset->get("custom_template/release"); - } - src_apk = src_apk.strip_edges(); - if (src_apk == "") { - if (p_debug) { - src_apk = find_export_template("android_debug.apk"); - } else { - src_apk = find_export_template("android_release.apk"); - } - if (src_apk == "") { - EditorNode::add_io_error(vformat(TTR("Package not found: %s"), src_apk)); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - if (ep.step(TTR("Creating APK..."), 0)) { - return ERR_SKIP; - } - - unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); - if (!pkg) { - EditorNode::add_io_error(vformat(TTR("Could not find template APK to export:\n%s"), src_apk)); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(pkg); - - zlib_filefunc_def io2 = io; - FileAccess *dst_f = nullptr; - io2.opaque = &dst_f; - - String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); - -#define CLEANUP_AND_RETURN(m_err) \ - { \ - DirAccess::remove_file_or_error(tmp_unaligned_path); \ - return m_err; \ - } - - zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); - - String cmdline = p_preset->get("command_line/extra_args"); - - String version_name = p_preset->get("version/name"); - String package_name = p_preset->get("package/unique_name"); - - String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - - Vector<String> invalid_abis(enabled_abis); - while (ret == UNZ_OK) { - //get filename - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); - - bool skip = false; - - String file = fname; - - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - - //write - if (file == "AndroidManifest.xml") { - _fix_manifest(p_preset, data, p_give_internet); - } - if (file == "resources.arsc") { - _fix_resources(p_preset, data); - } - - // Process the splash image - if ((file == SPLASH_IMAGE_EXPORT_PATH || file == LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH) && splash_image.is_valid() && !splash_image->is_empty()) { - _load_image_data(splash_image, data); - } - - // Process the splash bg color image - if ((file == SPLASH_BG_COLOR_PATH || file == LEGACY_BUILD_SPLASH_BG_COLOR_PATH) && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { - _load_image_data(splash_bg_color_image, data); - } - - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - if (file == launcher_icons[i].export_path) { - _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); - } - } - if (foreground.is_valid() && !foreground->is_empty()) { - if (file == launcher_adaptive_icon_foregrounds[i].export_path) { - _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); - } - } - if (background.is_valid() && !background->is_empty()) { - if (file == launcher_adaptive_icon_backgrounds[i].export_path) { - _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); - } - } - } - - if (file.ends_with(".so")) { - bool enabled = false; - for (int i = 0; i < enabled_abis.size(); ++i) { - if (file.begins_with("lib/" + enabled_abis[i] + "/")) { - invalid_abis.erase(enabled_abis[i]); - enabled = true; - break; - } - } - if (!enabled) { - skip = true; - } - } - - if (file.begins_with("META-INF") && should_sign) { - skip = true; - } - - if (!skip) { - print_line("ADDING: " + file); - - // Respect decision on compression made by AAPT for the export template - const bool uncompressed = info.compression_method == 0; - - zip_fileinfo zipfi = get_zip_fileinfo(); - - zipOpenNewFileInZip(unaligned_apk, - file.utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - uncompressed ? 0 : Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size()); - zipCloseFileInZip(unaligned_apk); - } - - ret = unzGoToNextFile(pkg); - } - - if (!invalid_abis.is_empty()) { - String unsupported_arch = String(", ").join(invalid_abis); - EditorNode::add_io_error(vformat(TTR("Missing libraries in the export template for the selected architectures: %s.\nPlease build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch)); - CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); - } - - if (ep.step(TTR("Adding files..."), 1)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - err = OK; - - if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { - APKExportData ed; - ed.ep = &ep; - ed.apk = unaligned_apk; - err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so); - } else { - if (apk_expansion) { - err = save_apk_expansion_file(p_preset, p_path); - if (err != OK) { - EditorNode::add_io_error(TTR("Could not write expansion package file!")); - return err; - } - } else { - APKExportData ed; - ed.ep = &ep; - ed.apk = unaligned_apk; - err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); - } - } - - if (err != OK) { - unzClose(pkg); - EditorNode::add_io_error(TTR("Could not export project files")); - CLEANUP_AND_RETURN(ERR_SKIP); - } - - zip_fileinfo zipfi = get_zip_fileinfo(); - zipOpenNewFileInZip(unaligned_apk, - "assets/_cl_", - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - 0, // No compress (little size gain and potentially slower startup) - Z_DEFAULT_COMPRESSION); - zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size()); - zipCloseFileInZip(unaligned_apk); - zipClose(unaligned_apk, nullptr); - unzClose(pkg); - - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - - // Let's zip-align (must be done before signing) - - static const int ZIP_ALIGNMENT = 4; - - // If we're not signing the apk, then the next step should be the last. - const int next_step = should_sign ? 103 : 105; - if (ep.step(TTR("Aligning APK..."), next_step)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io); - if (!tmp_unaligned) { - EditorNode::add_io_error(TTR("Could not unzip temporary unaligned APK.")); - CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); - } - - ret = unzGoToFirstFile(tmp_unaligned); - - io2 = io; - dst_f = nullptr; - io2.opaque = &dst_f; - zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); - - // Take files from the unaligned APK and write them out to the aligned one - // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed, - // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp - int bias = 0; - while (ret == UNZ_OK) { - unz_file_info info; - memset(&info, 0, sizeof(info)); - - char fname[16384]; - char extra[16384]; - ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0); - - String file = fname; - - Vector<uint8_t> data; - data.resize(info.compressed_size); - - // read - int method, level; - unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read - long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned); - unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size()); - unzCloseCurrentFile(tmp_unaligned); - - // align - int padding = 0; - if (!info.compression_method) { - // Uncompressed file => Align - long new_offset = file_offset + bias; - padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT; - } - - memset(extra + info.size_file_extra, 0, padding); - - zip_fileinfo fileinfo = get_zip_fileinfo(); - zipOpenNewFileInZip2(final_apk, - file.utf8().get_data(), - &fileinfo, - extra, - info.size_file_extra + padding, - nullptr, - 0, - nullptr, - method, - level, - 1); // raw write - zipWriteInFileInZip(final_apk, data.ptr(), data.size()); - zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc); - - bias += padding; - - ret = unzGoToNextFile(tmp_unaligned); - } - - zipClose(final_apk, nullptr); - unzClose(tmp_unaligned); - - if (should_sign) { - // Signing must be done last as any additional modifications to the - // file will invalidate the signature. - err = sign_apk(p_preset, p_debug, p_path, ep); - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - } - - CLEANUP_AND_RETURN(OK); - } - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("mobile"); - r_features->push_back("Android"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformAndroid() { - Ref<Image> img = memnew(Image(_android_logo)); - logo.instantiate(); - logo->create_from_image(img); - - img = Ref<Image>(memnew(Image(_android_run_icon))); - run_icon.instantiate(); - run_icon->create_from_image(img); - - devices_changed.set(); - plugins_changed.set(); - check_for_changes_thread.start(_check_for_changes_poll_thread, this); - } - - ~EditorExportPlatformAndroid() { - quit_request.set(); - check_for_changes_thread.wait_to_finish(); - } -}; +#include "export_plugin.h" void register_android_exporter() { String exe_ext; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp new file mode 100644 index 0000000000..6c2e481856 --- /dev/null +++ b/platform/android/export/export_plugin.cpp @@ -0,0 +1,2945 @@ +/*************************************************************************/ +/* export_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 "export_plugin.h" + +static const char *android_perms[] = { + "ACCESS_CHECKIN_PROPERTIES", + "ACCESS_COARSE_LOCATION", + "ACCESS_FINE_LOCATION", + "ACCESS_LOCATION_EXTRA_COMMANDS", + "ACCESS_MOCK_LOCATION", + "ACCESS_NETWORK_STATE", + "ACCESS_SURFACE_FLINGER", + "ACCESS_WIFI_STATE", + "ACCOUNT_MANAGER", + "ADD_VOICEMAIL", + "AUTHENTICATE_ACCOUNTS", + "BATTERY_STATS", + "BIND_ACCESSIBILITY_SERVICE", + "BIND_APPWIDGET", + "BIND_DEVICE_ADMIN", + "BIND_INPUT_METHOD", + "BIND_NFC_SERVICE", + "BIND_NOTIFICATION_LISTENER_SERVICE", + "BIND_PRINT_SERVICE", + "BIND_REMOTEVIEWS", + "BIND_TEXT_SERVICE", + "BIND_VPN_SERVICE", + "BIND_WALLPAPER", + "BLUETOOTH", + "BLUETOOTH_ADMIN", + "BLUETOOTH_PRIVILEGED", + "BRICK", + "BROADCAST_PACKAGE_REMOVED", + "BROADCAST_SMS", + "BROADCAST_STICKY", + "BROADCAST_WAP_PUSH", + "CALL_PHONE", + "CALL_PRIVILEGED", + "CAMERA", + "CAPTURE_AUDIO_OUTPUT", + "CAPTURE_SECURE_VIDEO_OUTPUT", + "CAPTURE_VIDEO_OUTPUT", + "CHANGE_COMPONENT_ENABLED_STATE", + "CHANGE_CONFIGURATION", + "CHANGE_NETWORK_STATE", + "CHANGE_WIFI_MULTICAST_STATE", + "CHANGE_WIFI_STATE", + "CLEAR_APP_CACHE", + "CLEAR_APP_USER_DATA", + "CONTROL_LOCATION_UPDATES", + "DELETE_CACHE_FILES", + "DELETE_PACKAGES", + "DEVICE_POWER", + "DIAGNOSTIC", + "DISABLE_KEYGUARD", + "DUMP", + "EXPAND_STATUS_BAR", + "FACTORY_TEST", + "FLASHLIGHT", + "FORCE_BACK", + "GET_ACCOUNTS", + "GET_PACKAGE_SIZE", + "GET_TASKS", + "GET_TOP_ACTIVITY_INFO", + "GLOBAL_SEARCH", + "HARDWARE_TEST", + "INJECT_EVENTS", + "INSTALL_LOCATION_PROVIDER", + "INSTALL_PACKAGES", + "INSTALL_SHORTCUT", + "INTERNAL_SYSTEM_WINDOW", + "INTERNET", + "KILL_BACKGROUND_PROCESSES", + "LOCATION_HARDWARE", + "MANAGE_ACCOUNTS", + "MANAGE_APP_TOKENS", + "MANAGE_DOCUMENTS", + "MASTER_CLEAR", + "MEDIA_CONTENT_CONTROL", + "MODIFY_AUDIO_SETTINGS", + "MODIFY_PHONE_STATE", + "MOUNT_FORMAT_FILESYSTEMS", + "MOUNT_UNMOUNT_FILESYSTEMS", + "NFC", + "PERSISTENT_ACTIVITY", + "PROCESS_OUTGOING_CALLS", + "READ_CALENDAR", + "READ_CALL_LOG", + "READ_CONTACTS", + "READ_EXTERNAL_STORAGE", + "READ_FRAME_BUFFER", + "READ_HISTORY_BOOKMARKS", + "READ_INPUT_STATE", + "READ_LOGS", + "READ_PHONE_STATE", + "READ_PROFILE", + "READ_SMS", + "READ_SOCIAL_STREAM", + "READ_SYNC_SETTINGS", + "READ_SYNC_STATS", + "READ_USER_DICTIONARY", + "REBOOT", + "RECEIVE_BOOT_COMPLETED", + "RECEIVE_MMS", + "RECEIVE_SMS", + "RECEIVE_WAP_PUSH", + "RECORD_AUDIO", + "REORDER_TASKS", + "RESTART_PACKAGES", + "SEND_RESPOND_VIA_MESSAGE", + "SEND_SMS", + "SET_ACTIVITY_WATCHER", + "SET_ALARM", + "SET_ALWAYS_FINISH", + "SET_ANIMATION_SCALE", + "SET_DEBUG_APP", + "SET_ORIENTATION", + "SET_POINTER_SPEED", + "SET_PREFERRED_APPLICATIONS", + "SET_PROCESS_LIMIT", + "SET_TIME", + "SET_TIME_ZONE", + "SET_WALLPAPER", + "SET_WALLPAPER_HINTS", + "SIGNAL_PERSISTENT_PROCESSES", + "STATUS_BAR", + "SUBSCRIBED_FEEDS_READ", + "SUBSCRIBED_FEEDS_WRITE", + "SYSTEM_ALERT_WINDOW", + "TRANSMIT_IR", + "UNINSTALL_SHORTCUT", + "UPDATE_DEVICE_STATS", + "USE_CREDENTIALS", + "USE_SIP", + "VIBRATE", + "WAKE_LOCK", + "WRITE_APN_SETTINGS", + "WRITE_CALENDAR", + "WRITE_CALL_LOG", + "WRITE_CONTACTS", + "WRITE_EXTERNAL_STORAGE", + "WRITE_GSERVICES", + "WRITE_HISTORY_BOOKMARKS", + "WRITE_PROFILE", + "WRITE_SECURE_SETTINGS", + "WRITE_SETTINGS", + "WRITE_SMS", + "WRITE_SOCIAL_STREAM", + "WRITE_SYNC_SETTINGS", + "WRITE_USER_DICTIONARY", + nullptr +}; + +static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png"; +static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png"; +static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png"; +static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png"; +static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml"; +static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json"; + +static const int icon_densities_count = 6; +static const char *launcher_icon_option = "launcher_icons/main_192x192"; +static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432"; +static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432"; + +static const LauncherIcon launcher_icons[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon.png", 192 }, + { "res/mipmap-xxhdpi-v4/icon.png", 144 }, + { "res/mipmap-xhdpi-v4/icon.png", 96 }, + { "res/mipmap-hdpi-v4/icon.png", 72 }, + { "res/mipmap-mdpi-v4/icon.png", 48 }, + { "res/mipmap/icon.png", 192 } +}; + +static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 }, + { "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 }, + { "res/mipmap-xhdpi-v4/icon_foreground.png", 216 }, + { "res/mipmap-hdpi-v4/icon_foreground.png", 162 }, + { "res/mipmap-mdpi-v4/icon_foreground.png", 108 }, + { "res/mipmap/icon_foreground.png", 432 } +}; + +static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon_background.png", 432 }, + { "res/mipmap-xxhdpi-v4/icon_background.png", 324 }, + { "res/mipmap-xhdpi-v4/icon_background.png", 216 }, + { "res/mipmap-hdpi-v4/icon_background.png", 162 }, + { "res/mipmap-mdpi-v4/icon_background.png", 108 }, + { "res/mipmap/icon_background.png", 432 } +}; + +static const int EXPORT_FORMAT_APK = 0; +static const int EXPORT_FORMAT_AAB = 1; + +void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { + EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; + + while (!ea->quit_request.is_set()) { + // Check for plugins updates + { + // Nothing to do if we already know the plugins have changed. + if (!ea->plugins_changed.is_set()) { + Vector<PluginConfigAndroid> loaded_plugins = get_plugins(); + + MutexLock lock(ea->plugins_lock); + + if (ea->plugins.size() != loaded_plugins.size()) { + ea->plugins_changed.set(); + } else { + for (int i = 0; i < ea->plugins.size(); i++) { + if (ea->plugins[i].name != loaded_plugins[i].name) { + ea->plugins_changed.set(); + break; + } + } + } + + if (ea->plugins_changed.is_set()) { + ea->plugins = loaded_plugins; + } + } + } + + // Check for devices updates + String adb = get_adb_path(); + if (FileAccess::exists(adb)) { + String devices; + List<String> args; + args.push_back("devices"); + int ec; + OS::get_singleton()->execute(adb, args, &devices, &ec); + + Vector<String> ds = devices.split("\n"); + Vector<String> ldevices; + for (int i = 1; i < ds.size(); i++) { + String d = ds[i]; + int dpos = d.find("device"); + if (dpos == -1) { + continue; + } + d = d.substr(0, dpos).strip_edges(); + ldevices.push_back(d); + } + + MutexLock lock(ea->device_lock); + + bool different = false; + + if (ea->devices.size() != ldevices.size()) { + different = true; + } else { + for (int i = 0; i < ea->devices.size(); i++) { + if (ea->devices[i].id != ldevices[i]) { + different = true; + break; + } + } + } + + if (different) { + Vector<Device> ndevices; + + for (int i = 0; i < ldevices.size(); i++) { + Device d; + d.id = ldevices[i]; + for (int j = 0; j < ea->devices.size(); j++) { + if (ea->devices[j].id == ldevices[i]) { + d.description = ea->devices[j].description; + d.name = ea->devices[j].name; + d.api_level = ea->devices[j].api_level; + } + } + + if (d.description == "") { + //in the oven, request! + args.clear(); + args.push_back("-s"); + args.push_back(d.id); + args.push_back("shell"); + args.push_back("getprop"); + int ec2; + String dp; + + OS::get_singleton()->execute(adb, args, &dp, &ec2); + + Vector<String> props = dp.split("\n"); + String vendor; + String device; + d.description = "Device ID: " + d.id + "\n"; + d.api_level = 0; + for (int j = 0; j < props.size(); j++) { + // got information by `shell cat /system/build.prop` before and its format is "property=value" + // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above + // its format is "[property]: [value]" so changed it as like build.prop + String p = props[j]; + p = p.replace("]: ", "="); + p = p.replace("[", ""); + p = p.replace("]", ""); + + if (p.begins_with("ro.product.model=")) { + device = p.get_slice("=", 1).strip_edges(); + } else if (p.begins_with("ro.product.brand=")) { + vendor = p.get_slice("=", 1).strip_edges().capitalize(); + } else if (p.begins_with("ro.build.display.id=")) { + d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.build.version.release=")) { + d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.build.version.sdk=")) { + d.api_level = p.get_slice("=", 1).to_int(); + } else if (p.begins_with("ro.product.cpu.abi=")) { + d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.product.manufacturer=")) { + d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.board.platform=")) { + d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.opengles.version=")) { + uint32_t opengl = p.get_slice("=", 1).to_int(); + d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n"; + } + } + + d.name = vendor + " " + device; + if (device == String()) { + continue; + } + } + + ndevices.push_back(d); + } + + ea->devices = ndevices; + ea->devices_changed.set(); + } + } + + uint64_t sleep = 200; + uint64_t wait = 3000000; + uint64_t time = OS::get_singleton()->get_ticks_usec(); + while (OS::get_singleton()->get_ticks_usec() - time < wait) { + OS::get_singleton()->delay_usec(1000 * sleep); + if (ea->quit_request.is_set()) { + break; + } + } + } + + if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { + String adb = get_adb_path(); + if (!FileAccess::exists(adb)) { + return; //adb not configured + } + + List<String> args; + args.push_back("kill-server"); + OS::get_singleton()->execute(adb, args); + }; +} + +String EditorExportPlatformAndroid::get_project_name(const String &p_name) const { + String aname; + if (p_name != "") { + aname = p_name; + } else { + aname = ProjectSettings::get_singleton()->get("application/config/name"); + } + + if (aname == "") { + aname = VERSION_NAME; + } + + return aname; +} + +String EditorExportPlatformAndroid::get_package_name(const String &p_package) const { + String pname = p_package; + String basename = ProjectSettings::get_singleton()->get("application/config/name"); + basename = basename.to_lower(); + + String name; + bool first = true; + for (int i = 0; i < basename.length(); i++) { + char32_t c = basename[i]; + if (c >= '0' && c <= '9' && first) { + continue; + } + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { + name += String::chr(c); + first = false; + } + } + if (name == "") { + name = "noname"; + } + + pname = pname.replace("$genname", name); + + return pname; +} + +bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const { + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = TTR("Package name is missing."); + } + return false; + } + + int segments = 0; + bool first = true; + for (int i = 0; i < pname.length(); i++) { + char32_t c = pname[i]; + if (first && c == '.') { + if (r_error) { + *r_error = TTR("Package segments must be of non-zero length."); + } + return false; + } + if (c == '.') { + segments++; + first = true; + continue; + } + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { + if (r_error) { + *r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c)); + } + return false; + } + if (first && (c >= '0' && c <= '9')) { + if (r_error) { + *r_error = TTR("A digit cannot be the first character in a package segment."); + } + return false; + } + if (first && c == '_') { + if (r_error) { + *r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c)); + } + return false; + } + first = false; + } + + if (segments == 0) { + if (r_error) { + *r_error = TTR("The package must have at least one '.' separator."); + } + return false; + } + + if (first) { + if (r_error) { + *r_error = TTR("Package segments must be of non-zero length."); + } + return false; + } + + return true; +} + +bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { + /* + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected attime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ + + // -- Unconditional uncompress to mimic AAPT plus some other + + static const char *unconditional_compress_ext[] = { + // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp + // These formats are already compressed, or don't compress well: + ".jpg", ".jpeg", ".png", ".gif", + ".wav", ".mp2", ".mp3", ".ogg", ".aac", + ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", + ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", + ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", + ".amr", ".awb", ".wma", ".wmv", + // Godot-specific: + ".webp", // Same reasoning as .png + ".cfb", // Don't let small config files slow-down startup + ".scn", // Binary scenes are usually already compressed + ".stex", // Streamable textures are usually already compressed + // Trailer for easier processing + nullptr + }; + + for (const char **ext = unconditional_compress_ext; *ext; ++ext) { + if (p_path.to_lower().ends_with(String(*ext))) { + return false; + } + } + + // -- Compressed resource? + + if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { + // Already compressed + return false; + } + + // --- TODO: Decide on texture resources according to their image compression setting + + return true; +} + +zip_fileinfo EditorExportPlatformAndroid::get_zip_fileinfo() { + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + zipfi.external_fa = 0; + zipfi.internal_fa = 0; + + return zipfi; +} + +Vector<String> EditorExportPlatformAndroid::get_abis() { + Vector<String> abis; + abis.push_back("armeabi-v7a"); + abis.push_back("arm64-v8a"); + abis.push_back("x86"); + abis.push_back("x86_64"); + return abis; +} + +/// List the gdap files in the directory specified by the p_path parameter. +Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path) { + Vector<String> dir_files; + DirAccessRef da = DirAccess::open(p_path); + if (da) { + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file == "") { + break; + } + + if (da->current_is_dir() || da->current_is_hidden()) { + continue; + } + + if (file.ends_with(PluginConfigAndroid::PLUGIN_CONFIG_EXT)) { + dir_files.push_back(file); + } + } + da->list_dir_end(); + } + + return dir_files; +} + +Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_plugins() { + Vector<PluginConfigAndroid> loaded_plugins; + + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins"); + + // Add the prebuilt plugins + loaded_plugins.append_array(PluginConfigAndroid::get_prebuilt_plugins(plugins_dir)); + + if (DirAccess::exists(plugins_dir)) { + Vector<String> plugins_filenames = list_gdap_files(plugins_dir); + + if (!plugins_filenames.is_empty()) { + Ref<ConfigFile> config_file = memnew(ConfigFile); + for (int i = 0; i < plugins_filenames.size(); i++) { + PluginConfigAndroid config = PluginConfigAndroid::load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + if (config.valid_config) { + loaded_plugins.push_back(config); + } else { + print_error("Invalid plugin config file " + plugins_filenames[i]); + } + } + } + } + + return loaded_plugins; +} + +Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { + Vector<PluginConfigAndroid> enabled_plugins; + Vector<PluginConfigAndroid> all_plugins = get_plugins(); + for (int i = 0; i < all_plugins.size(); i++) { + PluginConfigAndroid plugin = all_plugins[i]; + bool enabled = p_presets->get("plugins/" + plugin.name); + if (enabled) { + enabled_plugins.push_back(plugin); + } + } + + return enabled_plugins; +} + +Error EditorExportPlatformAndroid::store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method) { + zip_fileinfo zipfi = get_zip_fileinfo(); + zipOpenNewFileInZip(ed->apk, + p_path.utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + compression_method, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size()); + zipCloseFileInZip(ed->apk); + + return OK; +} + +Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObject &p_so) { + if (!p_so.path.get_file().begins_with("lib")) { + String err = "Android .so file names must start with \"lib\", but got: " + p_so.path; + ERR_PRINT(err); + return FAILED; + } + APKExportData *ed = (APKExportData *)p_userdata; + Vector<String> abis = get_abis(); + bool exported = false; + for (int i = 0; i < p_so.tags.size(); ++i) { + // shared objects can be fat (compatible with multiple ABIs) + int abi_index = abis.find(p_so.tags[i]); + if (abi_index != -1) { + exported = true; + String abi = abis[abi_index]; + String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file()); + Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); + Error store_err = store_in_apk(ed, dst_path, array); + ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'."); + } + } + if (!exported) { + String abis_string = String(" ").join(abis); + String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; + ERR_PRINT(err); + return FAILED; + } + return OK; +} + +Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + APKExportData *ed = (APKExportData *)p_userdata; + String dst_path = p_path.replace_first("res://", "assets/"); + + store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); + return OK; +} + +Error EditorExportPlatformAndroid::ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + return OK; +} + +Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const SharedObject &p_so) { + ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED, + "Android .so file names must start with \"lib\", but got: " + p_so.path); + Vector<String> abis = get_abis(); + CustomExportData *export_data = (CustomExportData *)p_userdata; + bool exported = false; + for (int i = 0; i < p_so.tags.size(); ++i) { + int abi_index = abis.find(p_so.tags[i]); + if (abi_index != -1) { + exported = true; + String base = "res://android/build/libs"; + String type = export_data->debug ? "debug" : "release"; + String abi = abis[abi_index]; + String filename = p_so.path.get_file(); + String dst_path = base.plus_file(type).plus_file(abi).plus_file(filename); + Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path); + print_verbose("Copying .so file from " + p_so.path + " to " + dst_path); + Error err = store_file_at_path(dst_path, data); + ERR_FAIL_COND_V_MSG(err, err, "Failed to copy .so file from " + p_so.path + " to " + dst_path); + export_data->libs.push_back(dst_path); + } + } + ERR_FAIL_COND_V_MSG(!exported, FAILED, + "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + String(" ").join(abis)); + return OK; +} + +void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { + const char **aperms = android_perms; + while (*aperms) { + bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); + if (enabled) { + r_permissions.push_back("android.permission." + String(*aperms)); + } + aperms++; + } + PackedStringArray user_perms = p_preset->get("permissions/custom_permissions"); + for (int i = 0; i < user_perms.size(); i++) { + String user_perm = user_perms[i].strip_edges(); + if (!user_perm.is_empty()) { + r_permissions.push_back(user_perm); + } + } + if (p_give_internet) { + if (r_permissions.find("android.permission.INTERNET") == -1) { + r_permissions.push_back("android.permission.INTERNET"); + } + } + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + if (xr_mode_index == 1 /* XRMode.OVR */) { + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index > 0) { + if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { + r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); + } + } + } +} + +void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { + print_verbose("Building temporary manifest.."); + String manifest_text = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " xmlns:tools=\"http://schemas.android.com/tools\">\n"; + + manifest_text += _get_screen_sizes_tag(p_preset); + manifest_text += _get_gles_tag(); + + Vector<String> perms; + _get_permissions(p_preset, p_give_internet, perms); + for (int i = 0; i < perms.size(); i++) { + manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i)); + } + + manifest_text += _get_xr_features_tag(p_preset); + manifest_text += _get_instrumentation_tag(p_preset); + manifest_text += _get_application_tag(p_preset); + manifest_text += "</manifest>\n"; + String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); + + print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text); + store_string_at_path(manifest_path, manifest_text); +} + +void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { + // Leaving the unused types commented because looking these constants up + // again later would be annoying + // const int CHUNK_AXML_FILE = 0x00080003; + // const int CHUNK_RESOURCEIDS = 0x00080180; + const int CHUNK_STRINGS = 0x001C0001; + // const int CHUNK_XML_END_NAMESPACE = 0x00100101; + const int CHUNK_XML_END_TAG = 0x00100103; + // const int CHUNK_XML_START_NAMESPACE = 0x00100100; + const int CHUNK_XML_START_TAG = 0x00100102; + // const int CHUNK_XML_TEXT = 0x00100104; + const int UTF8_FLAG = 0x00000100; + + Vector<String> string_table; + + uint32_t ofs = 8; + + uint32_t string_count = 0; + //uint32_t styles_count = 0; + uint32_t string_flags = 0; + uint32_t string_data_offset = 0; + + //uint32_t styles_offset = 0; + uint32_t string_table_begins = 0; + uint32_t string_table_ends = 0; + Vector<uint8_t> stable_extra; + + String version_name = p_preset->get("version/name"); + int version_code = p_preset->get("version/code"); + String package_name = p_preset->get("package/unique_name"); + + const int screen_orientation = + _get_android_orientation_value(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); + + bool screen_support_small = p_preset->get("screen/support_small"); + bool screen_support_normal = p_preset->get("screen/support_normal"); + bool screen_support_large = p_preset->get("screen/support_large"); + bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + + bool backup_allowed = p_preset->get("user_data_backup/allow"); + bool classify_as_game = p_preset->get("package/classify_as_game"); + + Vector<String> perms; + // Write permissions into the perms variable. + _get_permissions(p_preset, p_give_internet, perms); + + while (ofs < (uint32_t)p_manifest.size()) { + uint32_t chunk = decode_uint32(&p_manifest[ofs]); + uint32_t size = decode_uint32(&p_manifest[ofs + 4]); + + switch (chunk) { + case CHUNK_STRINGS: { + int iofs = ofs + 8; + + string_count = decode_uint32(&p_manifest[iofs]); + //styles_count = decode_uint32(&p_manifest[iofs + 4]); + string_flags = decode_uint32(&p_manifest[iofs + 8]); + string_data_offset = decode_uint32(&p_manifest[iofs + 12]); + //styles_offset = decode_uint32(&p_manifest[iofs + 16]); + /* + printf("string count: %i\n",string_count); + printf("flags: %i\n",string_flags); + printf("sdata ofs: %i\n",string_data_offset); + printf("styles ofs: %i\n",styles_offset); + */ + uint32_t st_offset = iofs + 20; + string_table.resize(string_count); + uint32_t string_end = 0; + + string_table_begins = st_offset; + + for (uint32_t i = 0; i < string_count; i++) { + uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]); + string_at += st_offset + string_count * 4; + + ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table."); + + if (string_flags & UTF8_FLAG) { + } else { + uint32_t len = decode_uint16(&p_manifest[string_at]); + Vector<char32_t> ucstring; + ucstring.resize(len + 1); + for (uint32_t j = 0; j < len; j++) { + uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); + ucstring.write[j] = c; + } + string_end = MAX(string_at + 2 + 2 * len, string_end); + ucstring.write[len] = 0; + string_table.write[i] = ucstring.ptr(); + } + } + + for (uint32_t i = string_end; i < (ofs + size); i++) { + stable_extra.push_back(p_manifest[i]); + } + + string_table_ends = ofs + size; + + } break; + case CHUNK_XML_START_TAG: { + int iofs = ofs + 8; + uint32_t name = decode_uint32(&p_manifest[iofs + 12]); + + String tname = string_table[name]; + uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]); + iofs += 28; + + for (uint32_t i = 0; i < attrcount; i++) { + uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]); + uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]); + uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]); + uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]); + + const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid); + String attrname = string_table[attr_name]; + const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : ""; + + //replace project information + if (tname == "manifest" && attrname == "package") { + string_table.write[attr_value] = get_package_name(package_name); + } + + if (tname == "manifest" && attrname == "versionCode") { + encode_uint32(version_code, &p_manifest.write[iofs + 16]); + } + + if (tname == "manifest" && attrname == "versionName") { + if (attr_value == 0xFFFFFFFF) { + WARN_PRINT("Version name in a resource, should be plain text"); + } else { + string_table.write[attr_value] = version_name; + } + } + + if (tname == "application" && attrname == "allowBackup") { + encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); + } + + if (tname == "application" && attrname == "isGame") { + encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); + } + + if (tname == "instrumentation" && attrname == "targetPackage") { + string_table.write[attr_value] = get_package_name(package_name); + } + + if (tname == "activity" && attrname == "screenOrientation") { + encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]); + } + + if (tname == "supports-screens") { + if (attrname == "smallScreens") { + encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + + } else if (attrname == "normalScreens") { + encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + + } else if (attrname == "largeScreens") { + encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + + } else if (attrname == "xlargeScreens") { + encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + } + } + + iofs += 20; + } + + } break; + case CHUNK_XML_END_TAG: { + int iofs = ofs + 8; + uint32_t name = decode_uint32(&p_manifest[iofs + 12]); + String tname = string_table[name]; + + if (tname == "uses-feature") { + Vector<String> feature_names; + Vector<bool> feature_required_list; + Vector<int> feature_versions; + + if (xr_mode_index == 1 /* XRMode.OVR */) { + // Check for hand tracking + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index > 0) { + feature_names.push_back("oculus.software.handtracking"); + feature_required_list.push_back(hand_tracking_index == 2); + feature_versions.push_back(-1); // no version attribute should be added. + } + } + + if (feature_names.size() > 0) { + ofs += 24; // skip over end tag + + // save manifest ending so we can restore it + Vector<uint8_t> manifest_end; + uint32_t manifest_cur_size = p_manifest.size(); + + manifest_end.resize(p_manifest.size() - ofs); + memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); + + int32_t attr_name_string = string_table.find("name"); + ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); + + int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); + if (ns_android_string == -1) { + string_table.push_back("http://schemas.android.com/apk/res/android"); + ns_android_string = string_table.size() - 1; + } + + int32_t attr_uses_feature_string = string_table.find("uses-feature"); + if (attr_uses_feature_string == -1) { + string_table.push_back("uses-feature"); + attr_uses_feature_string = string_table.size() - 1; + } + + int32_t attr_required_string = string_table.find("required"); + if (attr_required_string == -1) { + string_table.push_back("required"); + attr_required_string = string_table.size() - 1; + } + + for (int i = 0; i < feature_names.size(); i++) { + String feature_name = feature_names[i]; + bool feature_required = feature_required_list[i]; + int feature_version = feature_versions[i]; + bool has_version_attribute = feature_version != -1; + + print_line("Adding feature " + feature_name); + + int32_t feature_string = string_table.find(feature_name); + if (feature_string == -1) { + string_table.push_back(feature_name); + feature_string = string_table.size() - 1; + } + + String required_value_string = feature_required ? "true" : "false"; + int32_t required_value = string_table.find(required_value_string); + if (required_value == -1) { + string_table.push_back(required_value_string); + required_value = string_table.size() - 1; + } + + int32_t attr_version_string = -1; + int32_t version_value = -1; + int tag_size; + int attr_count; + if (has_version_attribute) { + attr_version_string = string_table.find("version"); + if (attr_version_string == -1) { + string_table.push_back("version"); + attr_version_string = string_table.size() - 1; + } + + version_value = string_table.find(itos(feature_version)); + if (version_value == -1) { + string_table.push_back(itos(feature_version)); + version_value = string_table.size() - 1; + } + + tag_size = 96; // node and three attrs + end node + attr_count = 3; + } else { + tag_size = 76; // node and two attrs + end node + attr_count = 2; + } + manifest_cur_size += tag_size + 24; + p_manifest.resize(manifest_cur_size); + + // start tag + encode_uint16(0x102, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name + encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size + encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index + encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index + encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index + + // android:name attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' + encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size + p_manifest.write[ofs + 50] = 0; // typedvalue_always0 + p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference + + // android:required attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns + encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' + encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size + p_manifest.write[ofs + 70] = 0; // typedvalue_always0 + p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) + encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference + + ofs += 76; + + if (has_version_attribute) { + // android:version attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns + encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name' + encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size + p_manifest.write[ofs + 14] = 0; // typedvalue_always0 + p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string) + encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference + + ofs += 20; + } + + // end tag + encode_uint16(0x103, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(24, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name + + ofs += 24; + } + memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); + ofs -= 24; // go back over back end + } + } + if (tname == "manifest") { + // save manifest ending so we can restore it + Vector<uint8_t> manifest_end; + uint32_t manifest_cur_size = p_manifest.size(); + + manifest_end.resize(p_manifest.size() - ofs); + memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); + + int32_t attr_name_string = string_table.find("name"); + ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); + + int32_t ns_android_string = string_table.find("android"); + ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace."); + + int32_t attr_uses_permission_string = string_table.find("uses-permission"); + if (attr_uses_permission_string == -1) { + string_table.push_back("uses-permission"); + attr_uses_permission_string = string_table.size() - 1; + } + + for (int i = 0; i < perms.size(); ++i) { + print_line("Adding permission " + perms[i]); + + manifest_cur_size += 56 + 24; // node + end node + p_manifest.resize(manifest_cur_size); + + // Add permission to the string pool + int32_t perm_string = string_table.find(perms[i]); + if (perm_string == -1) { + string_table.push_back(perms[i]); + perm_string = string_table.size() - 1; + } + + // start tag + encode_uint16(0x102, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(56, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size + encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index + encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index + encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index + + // attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' + encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size + p_manifest.write[ofs + 50] = 0; // typedvalue_always0 + p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference + + ofs += 56; + + // end tag + encode_uint16(0x103, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(24, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + + ofs += 24; + } + + // copy footer back in + memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); + } + } break; + } + + ofs += size; + } + + //create new andriodmanifest binary + + Vector<uint8_t> ret; + ret.resize(string_table_begins + string_table.size() * 4); + + for (uint32_t i = 0; i < string_table_begins; i++) { + ret.write[i] = p_manifest[i]; + } + + ofs = 0; + for (int i = 0; i < string_table.size(); i++) { + encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); + ofs += string_table[i].length() * 2 + 2 + 2; + } + + ret.resize(ret.size() + ofs); + string_data_offset = ret.size() - ofs; + uint8_t *chars = &ret.write[string_data_offset]; + for (int i = 0; i < string_table.size(); i++) { + String s = string_table[i]; + encode_uint16(s.length(), chars); + chars += 2; + for (int j = 0; j < s.length(); j++) { + encode_uint16(s[j], chars); + chars += 2; + } + encode_uint16(0, chars); + chars += 2; + } + + for (int i = 0; i < stable_extra.size(); i++) { + ret.push_back(stable_extra[i]); + } + + //pad + while (ret.size() % 4) { + ret.push_back(0); + } + + uint32_t new_stable_end = ret.size(); + + uint32_t extra = (p_manifest.size() - string_table_ends); + ret.resize(new_stable_end + extra); + for (uint32_t i = 0; i < extra; i++) { + ret.write[new_stable_end + i] = p_manifest[string_table_ends + i]; + } + + while (ret.size() % 4) { + ret.push_back(0); + } + encode_uint32(ret.size(), &ret.write[4]); //update new file size + + encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size + encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings + encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset + + p_manifest = ret; +} + +String EditorExportPlatformAndroid::_parse_string(const uint8_t *p_bytes, bool p_utf8) { + uint32_t offset = 0; + uint32_t len = 0; + + if (p_utf8) { + uint8_t byte = p_bytes[offset]; + if (byte & 0x80) { + offset += 2; + } else { + offset += 1; + } + byte = p_bytes[offset]; + offset++; + if (byte & 0x80) { + len = byte & 0x7F; + len = (len << 8) + p_bytes[offset]; + offset++; + } else { + len = byte; + } + } else { + len = decode_uint16(&p_bytes[offset]); + offset += 2; + if (len & 0x8000) { + len &= 0x7FFF; + len = (len << 16) + decode_uint16(&p_bytes[offset]); + offset += 2; + } + } + + if (p_utf8) { + Vector<uint8_t> str8; + str8.resize(len + 1); + for (uint32_t i = 0; i < len; i++) { + str8.write[i] = p_bytes[offset + i]; + } + str8.write[len] = 0; + String str; + str.parse_utf8((const char *)str8.ptr()); + return str; + } else { + String str; + for (uint32_t i = 0; i < len; i++) { + char32_t c = decode_uint16(&p_bytes[offset + i * 2]); + if (c == 0) { + break; + } + str += String::chr(c); + } + return str; + } +} + +void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) { + const int UTF8_FLAG = 0x00000100; + + uint32_t string_block_len = decode_uint32(&r_manifest[16]); + uint32_t string_count = decode_uint32(&r_manifest[20]); + uint32_t string_flags = decode_uint32(&r_manifest[28]); + const uint32_t string_table_begins = 40; + + Vector<String> string_table; + + String package_name = p_preset->get("package/name"); + + for (uint32_t i = 0; i < string_count; i++) { + uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]); + offset += string_table_begins + string_count * 4; + + String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG); + + if (str.begins_with("godot-project-name")) { + if (str == "godot-project-name") { + //project name + str = get_project_name(package_name); + + } else { + String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_"); + String prop = "application/config/name_" + lang; + if (ProjectSettings::get_singleton()->has_setting(prop)) { + str = ProjectSettings::get_singleton()->get(prop); + } else { + str = get_project_name(package_name); + } + } + } + + string_table.push_back(str); + } + + //write a new string table, but use 16 bits + Vector<uint8_t> ret; + ret.resize(string_table_begins + string_table.size() * 4); + + for (uint32_t i = 0; i < string_table_begins; i++) { + ret.write[i] = r_manifest[i]; + } + + int ofs = 0; + for (int i = 0; i < string_table.size(); i++) { + encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); + ofs += string_table[i].length() * 2 + 2 + 2; + } + + ret.resize(ret.size() + ofs); + uint8_t *chars = &ret.write[ret.size() - ofs]; + for (int i = 0; i < string_table.size(); i++) { + String s = string_table[i]; + encode_uint16(s.length(), chars); + chars += 2; + for (int j = 0; j < s.length(); j++) { + encode_uint16(s[j], chars); + chars += 2; + } + encode_uint16(0, chars); + chars += 2; + } + + //pad + while (ret.size() % 4) { + ret.push_back(0); + } + + //change flags to not use utf8 + encode_uint32(string_flags & ~0x100, &ret.write[28]); + //change length + encode_uint32(ret.size() - 12, &ret.write[16]); + //append the rest... + int rest_from = 12 + string_block_len; + int rest_to = ret.size(); + int rest_len = (r_manifest.size() - rest_from); + ret.resize(ret.size() + (r_manifest.size() - rest_from)); + for (int i = 0; i < rest_len; i++) { + ret.write[rest_to + i] = r_manifest[rest_from + i]; + } + //finally update the size + encode_uint32(ret.size(), &ret.write[4]); + + r_manifest = ret; + //printf("end\n"); +} + +void EditorExportPlatformAndroid::_load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) { + Vector<uint8_t> png_buffer; + Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer); + if (err == OK) { + p_data.resize(png_buffer.size()); + memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); + } else { + String err_str = String("Failed to convert splash image to png."); + WARN_PRINT(err_str.utf8().get_data()); + } +} + +void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) { + Ref<Image> working_image = p_source_image; + + if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) { + working_image = p_source_image->duplicate(); + working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS); + } + + Vector<uint8_t> png_buffer; + Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); + if (err == OK) { + p_data.resize(png_buffer.size()); + memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); + } else { + String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png."; + WARN_PRINT(err_str.utf8().get_data()); + } +} + +String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) { + bool scale_splash = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + bool apply_filter = ProjectSettings::get_singleton()->get("application/boot_splash/use_filter"); + String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + + if (!project_splash_path.is_empty()) { + splash_image.instantiate(); + print_verbose("Loading splash image: " + project_splash_path); + const Error err = ImageLoader::load_image(project_splash_path, splash_image); + if (err) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")"); + } + splash_image.unref(); + } + } + + if (splash_image.is_null()) { + // Use the default + print_verbose("Using default splash image."); + splash_image = Ref<Image>(memnew(Image(boot_splash_png))); + } + + if (scale_splash) { + Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + int width, height; + if (screen_size.width > screen_size.height) { + // scale horizontally + height = screen_size.height; + width = splash_image->get_width() * screen_size.height / splash_image->get_height(); + } else { + // scale vertically + width = screen_size.width; + height = splash_image->get_height() * screen_size.width / splash_image->get_width(); + } + splash_image->resize(width, height); + } + + // Setup the splash bg color + bool bg_color_valid; + Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); + if (!bg_color_valid) { + bg_color = boot_splash_bg_color; + } + + print_verbose("Creating splash background color image."); + splash_bg_color_image.instantiate(); + splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); + splash_bg_color_image->fill(bg_color); + + String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); + return processed_splash_config_xml; +} + +void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { + String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + + icon.instantiate(); + foreground.instantiate(); + background.instantiate(); + + // Regular icon: user selection -> project icon -> default. + String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); + print_verbose("Loading regular icon from " + path); + if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) { + print_verbose("- falling back to project icon: " + project_icon_path); + ImageLoader::load_image(project_icon_path, icon); + } + + // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); + print_verbose("Loading adaptive foreground icon from " + path); + if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) { + print_verbose("- falling back to using the regular icon"); + foreground = icon; + } + + // Adaptive background: user selection -> default. + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); + if (!path.is_empty()) { + print_verbose("Loading adaptive background icon from " + path); + ImageLoader::load_image(path, background); + } +} + +void EditorExportPlatformAndroid::store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) { + store_image(launcher_icon.export_path, data); +} + +void EditorExportPlatformAndroid::store_image(const String &export_path, const Vector<uint8_t> &data) { + String img_path = export_path.insert(0, "res://android/build/"); + store_file_at_path(img_path, data); +} + +void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, + const String &processed_splash_config_xml, + const Ref<Image> &splash_image, + const Ref<Image> &splash_bg_color_image, + const Ref<Image> &main_image, + const Ref<Image> &foreground, + const Ref<Image> &background) { + // Store the splash configuration + if (!processed_splash_config_xml.is_empty()) { + print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml); + store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml); + } + + // Store the splash image + if (splash_image.is_valid() && !splash_image->is_empty()) { + print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH)); + Vector<uint8_t> data; + _load_image_data(splash_image, data); + store_image(SPLASH_IMAGE_EXPORT_PATH, data); + } + + // Store the splash bg color image + if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { + print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH)); + Vector<uint8_t> data; + _load_image_data(splash_bg_color_image, data); + store_image(SPLASH_BG_COLOR_PATH, data); + } + + // Prepare images to be resized for the icons. If some image ends up being uninitialized, + // the default image from the export template will be used. + + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data); + store_image(launcher_icons[i], data); + } + + if (foreground.is_valid() && !foreground->is_empty()) { + print_verbose("Processing launcher adaptive icon foreground for dimension " + itos(launcher_adaptive_icon_foregrounds[i].dimensions) + " into " + launcher_adaptive_icon_foregrounds[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground, + launcher_adaptive_icon_foregrounds[i].dimensions, data); + store_image(launcher_adaptive_icon_foregrounds[i], data); + } + + if (background.is_valid() && !background->is_empty()) { + print_verbose("Processing launcher adaptive icon background for dimension " + itos(launcher_adaptive_icon_backgrounds[i].dimensions) + " into " + launcher_adaptive_icon_backgrounds[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background, + launcher_adaptive_icon_backgrounds[i].dimensions, data); + store_image(launcher_adaptive_icon_backgrounds[i], data); + } + } +} + +Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { + Vector<String> abis = get_abis(); + Vector<String> enabled_abis; + for (int i = 0; i < abis.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + abis[i]); + if (is_enabled) { + enabled_abis.push_back(abis[i]); + } + } + return enabled_abis; +} + +void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } + // FIXME: Review what texture formats are used for Vulkan. + if (driver == "Vulkan") { + r_features->push_back("etc2"); + } + + Vector<String> abis = get_enabled_abis(p_preset); + for (int i = 0; i < abis.size(); ++i) { + r_features->push_back(abis[i]); + } +} + +void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK)); + + Vector<PluginConfigAndroid> plugins_configs = get_plugins(); + for (int i = 0; i < plugins_configs.size(); i++) { + print_verbose("Found Android plugin " + plugins_configs[i].name); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false)); + } + plugins_changed.clear(); + + Vector<String> abis = get_abis(); + for (int i = 0; i < abis.size(); ++i) { + String abi = abis[i]; + bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a"); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray())); + + const char **perms = android_perms; + while (*perms) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false)); + perms++; + } +} + +String EditorExportPlatformAndroid::get_name() const { + return "Android"; +} + +String EditorExportPlatformAndroid::get_os_name() const { + return "Android"; +} + +Ref<Texture2D> EditorExportPlatformAndroid::get_logo() const { + return logo; +} + +bool EditorExportPlatformAndroid::should_update_export_options() { + bool export_options_changed = plugins_changed.is_set(); + if (export_options_changed) { + // don't clear unless we're reporting true, to avoid race + plugins_changed.clear(); + } + return export_options_changed; +} + +bool EditorExportPlatformAndroid::poll_export() { + bool dc = devices_changed.is_set(); + if (dc) { + // don't clear unless we're reporting true, to avoid race + devices_changed.clear(); + } + return dc; +} + +int EditorExportPlatformAndroid::get_options_count() const { + MutexLock lock(device_lock); + return devices.size(); +} + +String EditorExportPlatformAndroid::get_options_tooltip() const { + return TTR("Select device from the list"); +} + +String EditorExportPlatformAndroid::get_option_label(int p_index) const { + ERR_FAIL_INDEX_V(p_index, devices.size(), ""); + MutexLock lock(device_lock); + return devices[p_index].name; +} + +String EditorExportPlatformAndroid::get_option_tooltip(int p_index) const { + ERR_FAIL_INDEX_V(p_index, devices.size(), ""); + MutexLock lock(device_lock); + String s = devices[p_index].description; + if (devices.size() == 1) { + // Tooltip will be: + // Name + // Description + s = devices[p_index].name + "\n\n" + s; + } + return s; +} + +Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { + ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); + + String can_export_error; + bool can_export_missing_templates; + if (!can_export(p_preset, can_export_error, can_export_missing_templates)) { + EditorNode::add_io_error(can_export_error); + return ERR_UNCONFIGURED; + } + + MutexLock lock(device_lock); + + EditorProgress ep("run", vformat(TTR("Running on %s"), devices[p_device].name), 3); + + String adb = get_adb_path(); + + // Export_temp APK. + if (ep.step(TTR("Exporting APK..."), 0)) { + return ERR_SKIP; + } + + const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_reverse = devices[p_device].api_level >= 21; + + if (use_reverse) { + p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; + } + + String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); + +#define CLEANUP_AND_RETURN(m_err) \ + { \ + DirAccess::remove_file_or_error(tmp_export_path); \ + return m_err; \ + } + + // Export to temporary APK before sending to device. + Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags); + + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + List<String> args; + int rv; + String output; + + bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install"); + String version_name = p_preset->get("version/name"); + String package_name = p_preset->get("package/unique_name"); + + if (remove_prev) { + if (ep.step(TTR("Uninstalling..."), 1)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + + print_line("Uninstalling previous version: " + devices[p_device].name); + + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("uninstall"); + args.push_back(get_package_name(package_name)); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + } + + print_line("Installing to device (please wait...): " + devices[p_device].name); + if (ep.step(TTR("Installing to device, please wait..."), 2)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("install"); + args.push_back("-r"); + args.push_back(tmp_export_path); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + if (err || rv != 0) { + EditorNode::add_io_error(vformat(TTR("Could not install to device: %s"), output)); + CLEANUP_AND_RETURN(ERR_CANT_CREATE); + } + + if (use_remote) { + if (use_reverse) { + static const char *const msg = "--- Device API >= 21; debugging over USB ---"; + EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); + print_line(String(msg).to_upper()); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("--remove-all"); + output.clear(); + OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + + if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { + int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("tcp:" + itos(dbg_port)); + args.push_back("tcp:" + itos(dbg_port)); + + output.clear(); + OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + print_line("Reverse result: " + itos(rv)); + } + + if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { + int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("tcp:" + itos(fs_port)); + args.push_back("tcp:" + itos(fs_port)); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + print_line("Reverse result2: " + itos(rv)); + } + } else { + static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; + EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); + print_line(String(msg).to_upper()); + } + } + + if (ep.step(TTR("Running on device..."), 3)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("shell"); + args.push_back("am"); + args.push_back("start"); + if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 + args.push_back("--user"); + args.push_back("0"); + } + args.push_back("-a"); + args.push_back("android.intent.action.MAIN"); + args.push_back("-n"); + args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp"); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + if (err || rv != 0) { + EditorNode::add_io_error(TTR("Could not execute on device.")); + CLEANUP_AND_RETURN(ERR_CANT_CREATE); + } + + CLEANUP_AND_RETURN(OK); +#undef CLEANUP_AND_RETURN +} + +Ref<Texture2D> EditorExportPlatformAndroid::get_run_icon() const { + return run_icon; +} + +String EditorExportPlatformAndroid::get_adb_path() { + String exe_ext = ""; + if (OS::get_singleton()->get_name() == "Windows") { + exe_ext = ".exe"; + } + String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + return sdk_path.plus_file("platform-tools/adb" + exe_ext); +} + +String EditorExportPlatformAndroid::get_apksigner_path() { + String exe_ext = ""; + if (OS::get_singleton()->get_name() == "Windows") { + exe_ext = ".bat"; + } + String apksigner_command_name = "apksigner" + exe_ext; + String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + String apksigner_path = ""; + + Error errn; + String build_tools_dir = sdk_path.plus_file("build-tools"); + DirAccessRef da = DirAccess::open(build_tools_dir, &errn); + if (errn != OK) { + print_error("Unable to open Android 'build-tools' directory."); + return apksigner_path; + } + + // There are additional versions directories we need to go through. + da->list_dir_begin(); + String sub_dir = da->get_next(); + while (!sub_dir.is_empty()) { + if (!sub_dir.begins_with(".") && da->current_is_dir()) { + // Check if the tool is here. + String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name); + if (FileAccess::exists(tool_path)) { + apksigner_path = tool_path; + break; + } + } + sub_dir = da->get_next(); + } + da->list_dir_end(); + + if (apksigner_path.is_empty()) { + EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool.")); + } + + return apksigner_path; +} + +bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + if (!bool(p_preset->get("custom_template/use_custom_build"))) { + String template_err; + bool dvalid = false; + bool rvalid = false; + bool has_export_templates = false; + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + template_err += TTR("Custom debug template not found.") + "\n"; + } + } else { + has_export_templates |= exists_export_template("android_debug.apk", &template_err); + } + + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + template_err += TTR("Custom release template not found.") + "\n"; + } + } else { + has_export_templates |= exists_export_template("android_release.apk", &template_err); + } + + r_missing_templates = !has_export_templates; + valid = dvalid || rvalid || has_export_templates; + if (!valid) { + err += template_err; + } + } else { + bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle"); + if (!installed_android_build_template) { + r_missing_templates = !exists_export_template("android_source.zip", &err); + err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; + } else { + r_missing_templates = false; + } + + valid = installed_android_build_template && !r_missing_templates; + } + + // Validate the rest of the configuration. + + String dk = p_preset->get("keystore/debug"); + String dk_user = p_preset->get("keystore/debug_user"); + String dk_password = p_preset->get("keystore/debug_password"); + + if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) { + valid = false; + err += TTR("Either Debug Keystore, Debug User AND Debug Password settings must be configured OR none of them.") + "\n"; + } + + if (!FileAccess::exists(dk)) { + dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + if (!FileAccess::exists(dk)) { + valid = false; + err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n"; + } + } + + String rk = p_preset->get("keystore/release"); + String rk_user = p_preset->get("keystore/release_user"); + String rk_password = p_preset->get("keystore/release_password"); + + if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) { + valid = false; + err += TTR("Either Release Keystore, Release User AND Release Password settings must be configured OR none of them.") + "\n"; + } + + if (!rk.is_empty() && !FileAccess::exists(rk)) { + valid = false; + err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; + } + + String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + if (sdk_path == "") { + err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; + valid = false; + } else { + Error errn; + // Check for the platform-tools directory. + DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); + if (errn != OK) { + err += TTR("Invalid Android SDK path in Editor Settings."); + err += TTR("Missing 'platform-tools' directory!"); + err += "\n"; + valid = false; + } + + // Validate that adb is available + String adb_path = get_adb_path(); + if (!FileAccess::exists(adb_path)) { + err += TTR("Unable to find Android SDK platform-tools' adb command."); + err += TTR("Please check in the Android SDK directory specified in Editor Settings."); + err += "\n"; + valid = false; + } + + // Check for the build-tools directory. + DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn); + if (errn != OK) { + err += TTR("Invalid Android SDK path in Editor Settings."); + err += TTR("Missing 'build-tools' directory!"); + err += "\n"; + valid = false; + } + + // Validate that apksigner is available + String apksigner_path = get_apksigner_path(); + if (!FileAccess::exists(apksigner_path)) { + err += TTR("Unable to find Android SDK build-tools' apksigner command."); + err += TTR("Please check in the Android SDK directory specified in Editor Settings."); + err += "\n"; + valid = false; + } + } + + bool apk_expansion = p_preset->get("apk_expansion/enable"); + + if (apk_expansion) { + String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); + + if (apk_expansion_pkey == "") { + valid = false; + + err += TTR("Invalid public key for APK expansion.") + "\n"; + } + } + + String pn = p_preset->get("package/unique_name"); + String pn_err; + + if (!is_package_name_valid(get_package_name(pn), &pn_err)) { + valid = false; + err += TTR("Invalid package name:") + " " + pn_err + "\n"; + } + + String etc_error = test_etc2(); + if (etc_error != String()) { + valid = false; + err += etc_error; + } + + // Ensure that `Use Custom Build` is enabled if a plugin is selected. + String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset)); + bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); + if (!enabled_plugins_names.is_empty() && !custom_build_enabled) { + valid = false; + err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); + err += "\n"; + } + + // Validate the Xr features are properly populated + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + int hand_tracking = p_preset->get("xr_features/hand_tracking"); + if (xr_mode_index != /* XRMode.OVR*/ 1) { + if (hand_tracking > 0) { + valid = false; + err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); + err += "\n"; + } + } + + if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB && + !bool(p_preset->get("custom_template/use_custom_build"))) { + valid = false; + err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); + err += "\n"; + } + + r_error = err; + return valid; +} + +List<String> EditorExportPlatformAndroid::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("apk"); + list.push_back("aab"); + return list; +} + +String EditorExportPlatformAndroid::get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + int version_code = p_preset->get("version/code"); + String package_name = p_preset->get("package/unique_name"); + String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; + String fullpath = p_path.get_base_dir().plus_file(apk_file_name); + return fullpath; +} + +Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + String fullpath = get_apk_expansion_fullpath(p_preset, p_path); + Error err = save_pack(p_preset, fullpath); + return err; +} + +void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) { + String cmdline = p_preset->get("command_line/extra_args"); + Vector<String> command_line_strings = cmdline.strip_edges().split(" "); + for (int i = 0; i < command_line_strings.size(); i++) { + if (command_line_strings[i].strip_edges().length() == 0) { + command_line_strings.remove(i); + i--; + } + } + + gen_export_flags(command_line_strings, p_flags); + + bool apk_expansion = p_preset->get("apk_expansion/enable"); + if (apk_expansion) { + String fullpath = get_apk_expansion_fullpath(p_preset, p_path); + String apk_expansion_public_key = p_preset->get("apk_expansion/public_key"); + + command_line_strings.push_back("--use_apk_expansion"); + command_line_strings.push_back("--apk_expansion_md5"); + command_line_strings.push_back(FileAccess::get_md5(fullpath)); + command_line_strings.push_back("--apk_expansion_key"); + command_line_strings.push_back(apk_expansion_public_key.strip_edges()); + } + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + if (xr_mode_index == 1) { + command_line_strings.push_back("--xr_mode_ovr"); + } else { // XRMode.REGULAR is the default. + command_line_strings.push_back("--xr_mode_regular"); + } + + bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer"); + if (use_32_bit_framebuffer) { + command_line_strings.push_back("--use_depth_32"); + } + + bool immersive = p_preset->get("screen/immersive_mode"); + if (immersive) { + command_line_strings.push_back("--use_immersive"); + } + + bool debug_opengl = p_preset->get("graphics/opengl_debug"); + if (debug_opengl) { + command_line_strings.push_back("--debug_opengl"); + } + + if (command_line_strings.size()) { + r_command_line_flags.resize(4); + encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]); + for (int i = 0; i < command_line_strings.size(); i++) { + print_line(itos(i) + " param: " + command_line_strings[i]); + CharString command_line_argument = command_line_strings[i].utf8(); + int base = r_command_line_flags.size(); + int length = command_line_argument.length(); + if (length == 0) { + continue; + } + r_command_line_flags.resize(base + 4 + length); + encode_uint32(length, &r_command_line_flags.write[base]); + memcpy(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length); + } + } +} + +Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { + int export_format = int(p_preset->get("custom_template/export_format")); + String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; + String release_keystore = p_preset->get("keystore/release"); + String release_username = p_preset->get("keystore/release_user"); + String release_password = p_preset->get("keystore/release_password"); + + String apksigner = get_apksigner_path(); + print_verbose("Starting signing of the " + export_label + " binary using " + apksigner); + if (!FileAccess::exists(apksigner)) { + EditorNode::add_io_error(vformat(TTR("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting %s is unsigned."), export_label)); + return OK; + } + + String keystore; + String password; + String user; + if (p_debug) { + keystore = p_preset->get("keystore/debug"); + password = p_preset->get("keystore/debug_password"); + user = p_preset->get("keystore/debug_user"); + + if (keystore.is_empty()) { + keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); + user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + } + + if (ep.step(vformat(TTR("Signing debug %s..."), export_label), 104)) { + return ERR_SKIP; + } + + } else { + keystore = release_keystore; + password = release_password; + user = release_username; + + if (ep.step(vformat(TTR("Signing release %s..."), export_label), 104)) { + return ERR_SKIP; + } + } + + if (!FileAccess::exists(keystore)) { + EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); + return ERR_FILE_CANT_OPEN; + } + + String output; + List<String> args; + args.push_back("sign"); + args.push_back("--verbose"); + args.push_back("--ks"); + args.push_back(keystore); + args.push_back("--ks-pass"); + args.push_back("pass:" + password); + args.push_back("--ks-key-alias"); + args.push_back(user); + args.push_back(export_path); + if (p_debug) { + // We only print verbose logs for debug builds to avoid leaking release keystore credentials. + print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); + } + int retval; + output.clear(); + OS::get_singleton()->execute(apksigner, args, &output, &retval, true); + print_verbose(output); + if (retval) { + EditorNode::add_io_error(vformat(TTR("'apksigner' returned with error #%d"), retval)); + return ERR_CANT_CREATE; + } + + if (ep.step(vformat(TTR("Verifying %s..."), export_label), 105)) { + return ERR_SKIP; + } + + args.clear(); + args.push_back("verify"); + args.push_back("--verbose"); + args.push_back(export_path); + if (p_debug) { + print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); + } + + output.clear(); + OS::get_singleton()->execute(apksigner, args, &output, &retval, true); + print_verbose(output); + if (retval) { + EditorNode::add_io_error(vformat(TTR("'apksigner' verification of %s failed."), export_label)); + return ERR_CANT_CREATE; + } + + print_verbose("Successfully completed signing build."); + return OK; +} + +void EditorExportPlatformAndroid::_clear_assets_directory() { + DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da_res->dir_exists("res://android/build/assets")) { + print_verbose("Clearing assets directory.."); + DirAccessRef da_assets = DirAccess::open("res://android/build/assets"); + da_assets->erase_contents_recursive(); + da_res->remove("res://android/build/assets"); + } +} + +void EditorExportPlatformAndroid::_remove_copied_libs() { + print_verbose("Removing previously installed libraries..."); + Error error; + String libs_json = FileAccess::get_file_as_string(GDNATIVE_LIBS_PATH, &error); + if (error || libs_json.is_empty()) { + print_verbose("No previously installed libraries found"); + return; + } + + JSON json; + error = json.parse(libs_json); + ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message()); + + Vector<String> libs = json.get_data(); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + for (int i = 0; i < libs.size(); i++) { + print_verbose("Removing previously installed library " + libs[i]); + da->remove(libs[i]); + } + da->remove(GDNATIVE_LIBS_PATH); +} + +String EditorExportPlatformAndroid::join_list(List<String> parts, const String &separator) const { + String ret; + for (int i = 0; i < parts.size(); ++i) { + if (i > 0) { + ret += separator; + } + ret += parts[i]; + } + return ret; +} + +Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + int export_format = int(p_preset->get("custom_template/export_format")); + bool should_sign = p_preset->get("package/signed"); + return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags); +} + +Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_apk; + Error err; + + EditorProgress ep("export", TTR("Exporting for Android"), 105, true); + + bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build")); + bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); + bool apk_expansion = p_preset->get("apk_expansion/enable"); + Vector<String> enabled_abis = get_enabled_abis(p_preset); + + print_verbose("Exporting for Android..."); + print_verbose("- debug build: " + bool_to_string(p_debug)); + print_verbose("- export path: " + p_path); + print_verbose("- export format: " + itos(export_format)); + print_verbose("- sign build: " + bool_to_string(should_sign)); + print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); + print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); + print_verbose("- enabled abis: " + String(",").join(enabled_abis)); + print_verbose("- export filter: " + itos(p_preset->get_export_filter())); + print_verbose("- include filter: " + p_preset->get_include_filter()); + print_verbose("- exclude filter: " + p_preset->get_exclude_filter()); + + Ref<Image> splash_image; + Ref<Image> splash_bg_color_image; + String processed_splash_config_xml = load_splash_refs(splash_image, splash_bg_color_image); + + Ref<Image> main_image; + Ref<Image> foreground; + Ref<Image> background; + + load_icon_refs(p_preset, main_image, foreground, background); + + Vector<uint8_t> command_line_flags; + // Write command line flags into the command_line_flags variable. + get_command_line_flags(p_preset, p_path, p_flags, command_line_flags); + + if (export_format == EXPORT_FORMAT_AAB) { + if (!p_path.ends_with(".aab")) { + EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension.")); + return ERR_UNCONFIGURED; + } + if (apk_expansion) { + EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle.")); + return ERR_UNCONFIGURED; + } + } + if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) { + EditorNode::get_singleton()->show_warning( + TTR("Invalid filename! Android APK requires the *.apk extension.")); + return ERR_UNCONFIGURED; + } + if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) { + EditorNode::add_io_error(TTR("Unsupported export format!\n")); + return ERR_UNCONFIGURED; //TODO: is this the right error? + } + + if (use_custom_build) { + print_verbose("Starting custom build.."); + //test that installed build version is alright + { + print_verbose("Checking build version.."); + FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); + return ERR_UNCONFIGURED; + } + String version = f->get_line().strip_edges(); + print_verbose("- build version: " + version); + f->close(); + if (version != VERSION_FULL_CONFIG) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG)); + return ERR_UNCONFIGURED; + } + } + String sdk_path = EDITOR_GET("export/android/android_sdk_path"); + ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); + print_verbose("Android sdk path: " + sdk_path); + + // TODO: should we use "package/name" or "application/config/name"? + String project_name = get_project_name(p_preset->get("package/name")); + err = _create_project_name_strings_files(p_preset, project_name); //project name localization. + if (err != OK) { + EditorNode::add_io_error(TTR("Unable to overwrite res://android/build/res/*.xml files with project name")); + } + // Copies the project icon files into the appropriate Gradle project directory. + _copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background); + // Write an AndroidManifest.xml file into the Gradle project directory. + _write_tmp_manifest(p_preset, p_give_internet, p_debug); + + //stores all the project files inside the Gradle project directory. Also includes all ABIs + _clear_assets_directory(); + _remove_copied_libs(); + if (!apk_expansion) { + print_verbose("Exporting project files.."); + CustomExportData user_data; + user_data.debug = p_debug; + err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); + if (err != OK) { + EditorNode::add_io_error(TTR("Could not export project files to gradle project\n")); + return err; + } + if (user_data.libs.size() > 0) { + FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE); + JSON json; + fa->store_string(json.stringify(user_data.libs, "\t")); + fa->close(); + } + } else { + print_verbose("Saving apk expansion file.."); + err = save_apk_expansion_file(p_preset, p_path); + if (err != OK) { + EditorNode::add_io_error(TTR("Could not write expansion package file!")); + return err; + } + } + print_verbose("Storing command line flags.."); + store_file_at_path("res://android/build/assets/_cl_", command_line_flags); + + print_verbose("Updating ANDROID_HOME environment to " + sdk_path); + OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required + String build_command; + +#ifdef WINDOWS_ENABLED + build_command = "gradlew.bat"; +#else + build_command = "gradlew"; +#endif + + String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build"); + build_command = build_path.plus_file(build_command); + + String package_name = get_package_name(p_preset->get("package/unique_name")); + String version_code = itos(p_preset->get("version/code")); + String version_name = p_preset->get("version/name"); + String enabled_abi_string = String("|").join(enabled_abis); + String sign_flag = should_sign ? "true" : "false"; + String zipalign_flag = "true"; + + Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset); + String local_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins); + String remote_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins); + String custom_maven_repos = PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins); + bool clean_build_required = is_clean_build_required(enabled_plugins); + + List<String> cmdline; + if (clean_build_required) { + cmdline.push_back("clean"); + } + + String build_type = p_debug ? "Debug" : "Release"; + if (export_format == EXPORT_FORMAT_AAB) { + String bundle_build_command = vformat("bundle%s", build_type); + cmdline.push_back(bundle_build_command); + } else if (export_format == EXPORT_FORMAT_APK) { + String apk_build_command = vformat("assemble%s", build_type); + cmdline.push_back(apk_build_command); + } + + cmdline.push_back("-p"); // argument to specify the start directory. + cmdline.push_back(build_path); // start directory. + cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. + cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code. + cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name. + cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs. + cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. + cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. + cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. + cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. + cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. + cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG)); + + // NOTE: The release keystore is not included in the verbose logging + // to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting. + // Any non-sensitive additions to the command line arguments must be done above this section. + // Sensitive additions must be done below the logging statement. + print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" "))); + + if (should_sign) { + if (p_debug) { + String debug_keystore = p_preset->get("keystore/debug"); + String debug_password = p_preset->get("keystore/debug_password"); + String debug_user = p_preset->get("keystore/debug_user"); + + if (debug_keystore.is_empty()) { + debug_keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); + debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + } + + cmdline.push_back("-Pdebug_keystore_file=" + debug_keystore); // argument to specify the debug keystore file. + cmdline.push_back("-Pdebug_keystore_alias=" + debug_user); // argument to specify the debug keystore alias. + cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password. + } else { + // Pass the release keystore info as well + String release_keystore = p_preset->get("keystore/release"); + String release_username = p_preset->get("keystore/release_user"); + String release_password = p_preset->get("keystore/release_password"); + if (!FileAccess::exists(release_keystore)) { + EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); + return ERR_FILE_CANT_OPEN; + } + + cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file. + cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias. + cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specify the release keystore password. + } + } + + int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline); + if (result != 0) { + EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation.")); + return ERR_CANT_CREATE; + } + + List<String> copy_args; + String copy_command; + if (export_format == EXPORT_FORMAT_AAB) { + copy_command = vformat("copyAndRename%sAab", build_type); + } else if (export_format == EXPORT_FORMAT_APK) { + copy_command = vformat("copyAndRename%sApk", build_type); + } + + copy_args.push_back(copy_command); + + copy_args.push_back("-p"); // argument to specify the start directory. + copy_args.push_back(build_path); // start directory. + + String export_filename = p_path.get_file(); + String export_path = p_path.get_base_dir(); + if (export_path.is_rel_path()) { + export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); + } + export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); + + copy_args.push_back("-Pexport_path=file:" + export_path); + copy_args.push_back("-Pexport_filename=" + export_filename); + + print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" "))); + int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args); + if (copy_result != 0) { + EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs.")); + return ERR_CANT_CREATE; + } + + print_verbose("Successfully completed Android custom build."); + return OK; + } + // This is the start of the Legacy build system + print_verbose("Starting legacy build system.."); + if (p_debug) { + src_apk = p_preset->get("custom_template/debug"); + } else { + src_apk = p_preset->get("custom_template/release"); + } + src_apk = src_apk.strip_edges(); + if (src_apk == "") { + if (p_debug) { + src_apk = find_export_template("android_debug.apk"); + } else { + src_apk = find_export_template("android_release.apk"); + } + if (src_apk == "") { + EditorNode::add_io_error(vformat(TTR("Package not found: %s"), src_apk)); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + if (ep.step(TTR("Creating APK..."), 0)) { + return ERR_SKIP; + } + + unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); + if (!pkg) { + EditorNode::add_io_error(vformat(TTR("Could not find template APK to export:\n%s"), src_apk)); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(pkg); + + zlib_filefunc_def io2 = io; + FileAccess *dst_f = nullptr; + io2.opaque = &dst_f; + + String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); + +#define CLEANUP_AND_RETURN(m_err) \ + { \ + DirAccess::remove_file_or_error(tmp_unaligned_path); \ + return m_err; \ + } + + zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); + + String cmdline = p_preset->get("command_line/extra_args"); + + String version_name = p_preset->get("version/name"); + String package_name = p_preset->get("package/unique_name"); + + String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); + + Vector<String> invalid_abis(enabled_abis); + while (ret == UNZ_OK) { + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); + + bool skip = false; + + String file = fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + + //write + if (file == "AndroidManifest.xml") { + _fix_manifest(p_preset, data, p_give_internet); + } + if (file == "resources.arsc") { + _fix_resources(p_preset, data); + } + + // Process the splash image + if ((file == SPLASH_IMAGE_EXPORT_PATH || file == LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH) && splash_image.is_valid() && !splash_image->is_empty()) { + _load_image_data(splash_image, data); + } + + // Process the splash bg color image + if ((file == SPLASH_BG_COLOR_PATH || file == LEGACY_BUILD_SPLASH_BG_COLOR_PATH) && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { + _load_image_data(splash_bg_color_image, data); + } + + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + if (file == launcher_icons[i].export_path) { + _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + } + } + if (foreground.is_valid() && !foreground->is_empty()) { + if (file == launcher_adaptive_icon_foregrounds[i].export_path) { + _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + } + } + if (background.is_valid() && !background->is_empty()) { + if (file == launcher_adaptive_icon_backgrounds[i].export_path) { + _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + } + } + } + + if (file.ends_with(".so")) { + bool enabled = false; + for (int i = 0; i < enabled_abis.size(); ++i) { + if (file.begins_with("lib/" + enabled_abis[i] + "/")) { + invalid_abis.erase(enabled_abis[i]); + enabled = true; + break; + } + } + if (!enabled) { + skip = true; + } + } + + if (file.begins_with("META-INF") && should_sign) { + skip = true; + } + + if (!skip) { + print_line("ADDING: " + file); + + // Respect decision on compression made by AAPT for the export template + const bool uncompressed = info.compression_method == 0; + + zip_fileinfo zipfi = get_zip_fileinfo(); + + zipOpenNewFileInZip(unaligned_apk, + file.utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + uncompressed ? 0 : Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size()); + zipCloseFileInZip(unaligned_apk); + } + + ret = unzGoToNextFile(pkg); + } + + if (!invalid_abis.is_empty()) { + String unsupported_arch = String(", ").join(invalid_abis); + EditorNode::add_io_error(vformat(TTR("Missing libraries in the export template for the selected architectures: %s.\nPlease build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch)); + CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); + } + + if (ep.step(TTR("Adding files..."), 1)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + err = OK; + + if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { + APKExportData ed; + ed.ep = &ep; + ed.apk = unaligned_apk; + err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so); + } else { + if (apk_expansion) { + err = save_apk_expansion_file(p_preset, p_path); + if (err != OK) { + EditorNode::add_io_error(TTR("Could not write expansion package file!")); + return err; + } + } else { + APKExportData ed; + ed.ep = &ep; + ed.apk = unaligned_apk; + err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); + } + } + + if (err != OK) { + unzClose(pkg); + EditorNode::add_io_error(TTR("Could not export project files")); + CLEANUP_AND_RETURN(ERR_SKIP); + } + + zip_fileinfo zipfi = get_zip_fileinfo(); + zipOpenNewFileInZip(unaligned_apk, + "assets/_cl_", + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + 0, // No compress (little size gain and potentially slower startup) + Z_DEFAULT_COMPRESSION); + zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size()); + zipCloseFileInZip(unaligned_apk); + zipClose(unaligned_apk, nullptr); + unzClose(pkg); + + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + // Let's zip-align (must be done before signing) + + static const int ZIP_ALIGNMENT = 4; + + // If we're not signing the apk, then the next step should be the last. + const int next_step = should_sign ? 103 : 105; + if (ep.step(TTR("Aligning APK..."), next_step)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + + unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io); + if (!tmp_unaligned) { + EditorNode::add_io_error(TTR("Could not unzip temporary unaligned APK.")); + CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); + } + + ret = unzGoToFirstFile(tmp_unaligned); + + io2 = io; + dst_f = nullptr; + io2.opaque = &dst_f; + zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); + + // Take files from the unaligned APK and write them out to the aligned one + // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed, + // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp + int bias = 0; + while (ret == UNZ_OK) { + unz_file_info info; + memset(&info, 0, sizeof(info)); + + char fname[16384]; + char extra[16384]; + ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0); + + String file = fname; + + Vector<uint8_t> data; + data.resize(info.compressed_size); + + // read + int method, level; + unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read + long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned); + unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size()); + unzCloseCurrentFile(tmp_unaligned); + + // align + int padding = 0; + if (!info.compression_method) { + // Uncompressed file => Align + long new_offset = file_offset + bias; + padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT; + } + + memset(extra + info.size_file_extra, 0, padding); + + zip_fileinfo fileinfo = get_zip_fileinfo(); + zipOpenNewFileInZip2(final_apk, + file.utf8().get_data(), + &fileinfo, + extra, + info.size_file_extra + padding, + nullptr, + 0, + nullptr, + method, + level, + 1); // raw write + zipWriteInFileInZip(final_apk, data.ptr(), data.size()); + zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc); + + bias += padding; + + ret = unzGoToNextFile(tmp_unaligned); + } + + zipClose(final_apk, nullptr); + unzClose(tmp_unaligned); + + if (should_sign) { + // Signing must be done last as any additional modifications to the + // file will invalidate the signature. + err = sign_apk(p_preset, p_debug, p_path, ep); + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + } + + CLEANUP_AND_RETURN(OK); +} + +void EditorExportPlatformAndroid::get_platform_features(List<String> *r_features) { + r_features->push_back("mobile"); + r_features->push_back("Android"); +} + +void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { +} + +EditorExportPlatformAndroid::EditorExportPlatformAndroid() { + Ref<Image> img = memnew(Image(_android_logo)); + logo.instantiate(); + logo->create_from_image(img); + + img = Ref<Image>(memnew(Image(_android_run_icon))); + run_icon.instantiate(); + run_icon->create_from_image(img); + + devices_changed.set(); + plugins_changed.set(); + check_for_changes_thread.start(_check_for_changes_poll_thread, this); +} + +EditorExportPlatformAndroid::~EditorExportPlatformAndroid() { + quit_request.set(); + check_for_changes_thread.wait_to_finish(); +} diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h new file mode 100644 index 0000000000..909428c2fe --- /dev/null +++ b/platform/android/export/export_plugin.h @@ -0,0 +1,255 @@ +/*************************************************************************/ +/* export_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. */ +/*************************************************************************/ + +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/image_loader.h" +#include "core/io/json.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/os/os.h" +#include "core/templates/safe_refcount.h" +#include "core/version.h" +#include "drivers/png/png_driver_common.h" +#include "editor/editor_export.h" +#include "editor/editor_log.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "main/splash.gen.h" +#include "platform/android/logo.gen.h" +#include "platform/android/run_icon.gen.h" + +#include "godot_plugin_config.h" +#include "gradle_export_util.h" + +#include <string.h> + +const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/splash_bg_color" /> + <item> + <bitmap + android:gravity="center" + android:filter="%s" + android:src="@drawable/splash" /> + </item> +</layer-list> +)SPLASH"; + +struct LauncherIcon { + const char *export_path; + int dimensions = 0; +}; + +class EditorExportPlatformAndroid : public EditorExportPlatform { + GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform); + + Ref<ImageTexture> logo; + Ref<ImageTexture> run_icon; + + struct Device { + String id; + String name; + String description; + int api_level = 0; + }; + + struct APKExportData { + zipFile apk; + EditorProgress *ep = nullptr; + }; + + struct CustomExportData { + bool debug; + Vector<String> libs; + }; + + Vector<PluginConfigAndroid> plugins; + String last_plugin_names; + uint64_t last_custom_build_time = 0; + SafeFlag plugins_changed; + Mutex plugins_lock; + Vector<Device> devices; + SafeFlag devices_changed; + Mutex device_lock; + Thread check_for_changes_thread; + SafeFlag quit_request; + + static void _check_for_changes_poll_thread(void *ud); + + String get_project_name(const String &p_name) const; + + String get_package_name(const String &p_package) const; + + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const; + + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data); + + static zip_fileinfo get_zip_fileinfo(); + + static Vector<String> get_abis(); + + /// List the gdap files in the directory specified by the p_path parameter. + static Vector<String> list_gdap_files(const String &p_path); + + static Vector<PluginConfigAndroid> get_plugins(); + + static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets); + + static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED); + + static Error save_apk_so(void *p_userdata, const SharedObject &p_so); + + static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + + static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + + static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so); + + void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions); + + void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug); + + void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet); + + static String _parse_string(const uint8_t *p_bytes, bool p_utf8); + + void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest); + + void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data); + + void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data); + + String load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image); + + void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background); + + void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data); + + void store_image(const String &export_path, const Vector<uint8_t> &data); + + void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, + const String &processed_splash_config_xml, + const Ref<Image> &splash_image, + const Ref<Image> &splash_bg_color_image, + const Ref<Image> &main_image, + const Ref<Image> &foreground, + const Ref<Image> &background); + + static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset); + +public: + typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + +public: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + + virtual void get_export_options(List<ExportOption> *r_options) override; + + virtual String get_name() const override; + + virtual String get_os_name() const override; + + virtual Ref<Texture2D> get_logo() const override; + + virtual bool should_update_export_options() override; + + virtual bool poll_export() override; + + virtual int get_options_count() const override; + + virtual String get_options_tooltip() const override; + + virtual String get_option_label(int p_index) const override; + + virtual String get_option_tooltip(int p_index) const override; + + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + + virtual Ref<Texture2D> get_run_icon() const override; + + static String get_adb_path(); + + static String get_apksigner_path(); + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + + inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) { + String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins); + bool first_build = last_custom_build_time == 0; + bool have_plugins_changed = false; + + if (!first_build) { + have_plugins_changed = plugin_names != last_plugin_names; + if (!have_plugins_changed) { + for (int i = 0; i < enabled_plugins.size(); i++) { + if (enabled_plugins.get(i).last_updated > last_custom_build_time) { + have_plugins_changed = true; + break; + } + } + } + } + + last_custom_build_time = OS::get_singleton()->get_unix_time(); + last_plugin_names = plugin_names; + + return have_plugins_changed || first_build; + } + + String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path); + + Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path); + + void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags); + + Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep); + + void _clear_assets_directory(); + + void _remove_copied_libs(); + + String join_list(List<String> parts, const String &separator) const; + + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags); + + virtual void get_platform_features(List<String> *r_features) override; + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override; + + EditorExportPlatformAndroid(); + + ~EditorExportPlatformAndroid(); +}; diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/export/godot_plugin_config.cpp index 6b708548ae..ba7b8ce6c7 100644 --- a/platform/android/plugin/godot_plugin_config.h +++ b/platform/android/export/godot_plugin_config.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* godot_plugin_config.h */ +/* godot_plugin_config.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,78 +28,23 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GODOT_PLUGIN_CONFIG_H -#define GODOT_PLUGIN_CONFIG_H - -#include "core/error/error_list.h" -#include "core/io/config_file.h" -#include "core/string/ustring.h" - -/* - The `config` section and fields are required and defined as follow: -- **name**: name of the plugin. -- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field. -- **binary**: - - if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`). - - if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0"). - -The `dependencies` section and fields are optional and defined as follow: -- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory. -- **remote**: contains a list of remote binary gradle dependencies for the plugin. -- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies. - - See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871 - */ -struct PluginConfigAndroid { - inline static const char *PLUGIN_CONFIG_EXT = ".gdap"; - - inline static const char *CONFIG_SECTION = "config"; - inline static const char *CONFIG_NAME_KEY = "name"; - inline static const char *CONFIG_BINARY_TYPE_KEY = "binary_type"; - inline static const char *CONFIG_BINARY_KEY = "binary"; - - inline static const char *DEPENDENCIES_SECTION = "dependencies"; - inline static const char *DEPENDENCIES_LOCAL_KEY = "local"; - inline static const char *DEPENDENCIES_REMOTE_KEY = "remote"; - inline static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos"; - - inline static const char *BINARY_TYPE_LOCAL = "local"; - inline static const char *BINARY_TYPE_REMOTE = "remote"; - - inline static const char *PLUGIN_VALUE_SEPARATOR = "|"; - - // Set to true when the config file is properly loaded. - bool valid_config = false; - // Unix timestamp of last change to this plugin. - uint64_t last_updated = 0; - - // Required config section - String name; - String binary_type; - String binary; - - // Optional dependencies section - Vector<String> local_dependencies; - Vector<String> remote_dependencies; - Vector<String> custom_maven_repos; -}; - +#include "godot_plugin_config.h" /* * Set of prebuilt plugins. * Currently unused, this is just for future reference: */ // static const PluginConfigAndroid MY_PREBUILT_PLUGIN = { -// /*.valid_config =*/true, -// /*.last_updated =*/0, -// /*.name =*/"GodotPayment", -// /*.binary_type =*/"local", -// /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar", -// /*.local_dependencies =*/{}, -// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"), -// /*.custom_maven_repos =*/{} +// /*.valid_config =*/true, +// /*.last_updated =*/0, +// /*.name =*/"GodotPayment", +// /*.binary_type =*/"local", +// /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar", +// /*.local_dependencies =*/{}, +// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"), +// /*.custom_maven_repos =*/{} // }; -static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { +String PluginConfigAndroid::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { String absolute_path; if (!dependency_path.is_empty()) { if (dependency_path.is_absolute_path()) { @@ -112,7 +57,7 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str return absolute_path; } -static inline PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) { +PluginConfigAndroid PluginConfigAndroid::resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) { PluginConfigAndroid resolved = prebuilt_plugin; resolved.binary = resolved.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary; if (!prebuilt_plugin.local_dependencies.is_empty()) { @@ -124,13 +69,13 @@ static inline PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid pr return resolved; } -static inline Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir) { +Vector<PluginConfigAndroid> PluginConfigAndroid::get_prebuilt_plugins(String plugins_base_dir) { Vector<PluginConfigAndroid> prebuilt_plugins; // prebuilt_plugins.push_back(resolve_prebuilt_plugin(MY_PREBUILT_PLUGIN, plugins_base_dir)); return prebuilt_plugins; } -static inline bool is_plugin_config_valid(PluginConfigAndroid plugin_config) { +bool PluginConfigAndroid::is_plugin_config_valid(PluginConfigAndroid plugin_config) { bool valid_name = !plugin_config.name.is_empty(); bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL || plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE; @@ -155,7 +100,7 @@ static inline bool is_plugin_config_valid(PluginConfigAndroid plugin_config) { return valid_name && valid_binary && valid_binary_type && valid_local_dependencies; } -static inline uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) { +uint64_t PluginConfigAndroid::get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) { uint64_t last_updated = FileAccess::get_modified_time(config_path); last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary)); @@ -167,7 +112,7 @@ static inline uint64_t get_plugin_modification_time(const PluginConfigAndroid &p return last_updated; } -static inline PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path) { +PluginConfigAndroid PluginConfigAndroid::load_plugin_config(Ref<ConfigFile> config_file, const String &path) { PluginConfigAndroid plugin_config = {}; if (config_file.is_valid()) { @@ -201,7 +146,7 @@ static inline PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file return plugin_config; } -static inline String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) { +String PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) { String plugins_binaries; if (!plugins_configs.is_empty()) { Vector<String> binaries; @@ -230,7 +175,7 @@ static inline String get_plugins_binaries(String binary_type, Vector<PluginConfi return plugins_binaries; } -static inline String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) { +String PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) { String custom_maven_repos; if (!plugins_configs.is_empty()) { Vector<String> repos_urls; @@ -248,7 +193,7 @@ static inline String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> return custom_maven_repos; } -static inline String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) { +String PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) { String plugins_names; if (!plugins_configs.is_empty()) { Vector<String> names; @@ -265,5 +210,3 @@ static inline String get_plugins_names(Vector<PluginConfigAndroid> plugins_confi return plugins_names; } - -#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/export/godot_plugin_config.h b/platform/android/export/godot_plugin_config.h new file mode 100644 index 0000000000..c016634371 --- /dev/null +++ b/platform/android/export/godot_plugin_config.h @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* godot_plugin_config.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 ANDROID_GODOT_PLUGIN_CONFIG_H +#define ANDROID_GODOT_PLUGIN_CONFIG_H + +#include "core/config/project_settings.h" +#include "core/error/error_list.h" +#include "core/io/config_file.h" +#include "core/string/ustring.h" + +/* + The `config` section and fields are required and defined as follow: +- **name**: name of the plugin. +- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field. +- **binary**: + - if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`). + - if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0"). + +The `dependencies` section and fields are optional and defined as follow: +- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory. +- **remote**: contains a list of remote binary gradle dependencies for the plugin. +- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies. + + See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871 + */ +struct PluginConfigAndroid { + inline static const char *PLUGIN_CONFIG_EXT = ".gdap"; + + inline static const char *CONFIG_SECTION = "config"; + inline static const char *CONFIG_NAME_KEY = "name"; + inline static const char *CONFIG_BINARY_TYPE_KEY = "binary_type"; + inline static const char *CONFIG_BINARY_KEY = "binary"; + + inline static const char *DEPENDENCIES_SECTION = "dependencies"; + inline static const char *DEPENDENCIES_LOCAL_KEY = "local"; + inline static const char *DEPENDENCIES_REMOTE_KEY = "remote"; + inline static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos"; + + inline static const char *BINARY_TYPE_LOCAL = "local"; + inline static const char *BINARY_TYPE_REMOTE = "remote"; + + inline static const char *PLUGIN_VALUE_SEPARATOR = "|"; + + // Set to true when the config file is properly loaded. + bool valid_config = false; + // Unix timestamp of last change to this plugin. + uint64_t last_updated = 0; + + // Required config section + String name; + String binary_type; + String binary; + + // Optional dependencies section + Vector<String> local_dependencies; + Vector<String> remote_dependencies; + Vector<String> custom_maven_repos; + + static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path); + + static PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir); + + static Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir); + + static bool is_plugin_config_valid(PluginConfigAndroid plugin_config); + + static uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path); + + static PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path); + + static String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs); + + static String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs); + + static String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs); +}; + +#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp new file mode 100644 index 0000000000..354287d872 --- /dev/null +++ b/platform/android/export/gradle_export_util.cpp @@ -0,0 +1,252 @@ +/*************************************************************************/ +/* gradle_export_util.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 "gradle_export_util.h" + +int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) { + switch (screen_orientation) { + case DisplayServer::SCREEN_PORTRAIT: + return 1; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + return 8; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + return 9; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + return 11; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + return 12; + case DisplayServer::SCREEN_SENSOR: + return 13; + case DisplayServer::SCREEN_LANDSCAPE: + default: + return 0; + } +} + +String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) { + switch (screen_orientation) { + case DisplayServer::SCREEN_PORTRAIT: + return "portrait"; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + return "reverseLandscape"; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + return "reversePortrait"; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + return "userLandscape"; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + return "userPortrait"; + case DisplayServer::SCREEN_SENSOR: + return "fullUser"; + case DisplayServer::SCREEN_LANDSCAPE: + default: + return "landscape"; + } +} + +// Utility method used to create a directory. +Error create_directory(const String &p_dir) { + if (!DirAccess::exists(p_dir)) { + DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); + Error err = filesystem_da->make_dir_recursive(p_dir); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); + memdelete(filesystem_da); + } + return OK; +} + +// Writes p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + if (err != OK) { + return err; + } + FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + fa->store_buffer(p_data.ptr(), p_data.size()); + memdelete(fa); + return OK; +} + +// Writes string p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_string_at_path(const String &p_path, const String &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + if (err != OK) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("Unable to write data into " + p_path); + } + return err; + } + FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + fa->store_string(p_data); + memdelete(fa); + return OK; +} + +// Implementation of EditorExportSaveFunction. +// This method will only be called as an input to export_project_files. +// It is used by the export_project_files method to save all the asset files into the gradle project. +// It's functionality mirrors that of the method save_apk_file. +// This method will be called ONLY when custom build is enabled. +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); + print_verbose("Saving project files from " + p_path + " into " + dst_path); + Error err = store_file_at_path(dst_path, p_data); + return err; +} + +// Creates strings.xml files inside the gradle project for different locales. +Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) { + print_verbose("Creating strings resources for supported locales for project " + project_name); + // Stores the string into the default values directory. + String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true)); + store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string); + + // Searches the Gradle project res/ directory to find all supported locales + DirAccessRef da = DirAccess::open("res://android/build/res"); + if (!da) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("Unable to open Android resources directory."); + } + return ERR_CANT_OPEN; + } + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file == "") { + break; + } + if (!file.begins_with("values-")) { + // NOTE: This assumes all directories that start with "values-" are for localization. + continue; + } + String locale = file.replace("values-", "").replace("-r", "_"); + String property_name = "application/config/name_" + locale; + String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml"; + if (ProjectSettings::get_singleton()->has_setting(property_name)) { + String locale_project_name = ProjectSettings::get_singleton()->get(property_name); + String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true)); + print_verbose("Storing project name for locale " + locale + " under " + locale_directory); + store_string_at_path(locale_directory, processed_xml_string); + } else { + // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch + store_string_at_path(locale_directory, processed_default_xml_string); + } + } + da->list_dir_end(); + return OK; +} + +String bool_to_string(bool v) { + return v ? "true" : "false"; +} + +String _get_gles_tag() { + bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" && + !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2"); + return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : ""; +} + +String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\""; + String sizes[] = { "small", "normal", "large", "xlarge" }; + size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]); + for (size_t i = 0; i < num_sizes; i++) { + String feature_name = vformat("screen/support_%s", sizes[i]); + String feature_support = bool_to_string(p_preset->get(feature_name)); + String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support); + manifest_screen_sizes += xml_entry; + } + manifest_screen_sizes += " />\n"; + return manifest_screen_sizes; +} + +String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_xr_features; + bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + if (uses_xr) { + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index == 1) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; + } else if (hand_tracking_index == 2) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n"; + } + } + return manifest_xr_features; +} + +String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) { + String package_name = p_preset->get("package/unique_name"); + String manifest_instrumentation_text = vformat( + " <instrumentation\n" + " tools:node=\"replace\"\n" + " android:name=\".GodotInstrumentation\"\n" + " android:icon=\"@mipmap/icon\"\n" + " android:label=\"@string/godot_project_name_string\"\n" + " android:targetPackage=\"%s\" />\n", + package_name); + return manifest_instrumentation_text; +} + +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { + bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); + String manifest_activity_text = vformat( + " <activity android:name=\"com.godot.game.GodotApp\" " + "tools:replace=\"android:screenOrientation\" " + "android:screenOrientation=\"%s\">\n", + orientation); + if (!uses_xr) { + manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; + } + manifest_activity_text += " </activity>\n"; + return manifest_activity_text; +} + +String _get_application_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_application_text = vformat( + " <application android:label=\"@string/godot_project_name_string\"\n" + " android:allowBackup=\"%s\"\n" + " android:icon=\"@mipmap/icon\"\n" + " android:isGame=\"%s\"\n" + " tools:replace=\"android:allowBackup,android:isGame\"\n" + " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", + bool_to_string(p_preset->get("user_data_backup/allow")), + bool_to_string(p_preset->get("package/classify_as_game"))); + + manifest_application_text += _get_activity_tag(p_preset); + manifest_application_text += " </application>\n"; + return manifest_application_text; +} diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 52a7e4c5cf..44e9a1727d 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -44,225 +44,43 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut </resources> )"; -int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) { - switch (screen_orientation) { - case DisplayServer::SCREEN_PORTRAIT: - return 1; - case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - return 8; - case DisplayServer::SCREEN_REVERSE_PORTRAIT: - return 9; - case DisplayServer::SCREEN_SENSOR_LANDSCAPE: - return 11; - case DisplayServer::SCREEN_SENSOR_PORTRAIT: - return 12; - case DisplayServer::SCREEN_SENSOR: - return 13; - case DisplayServer::SCREEN_LANDSCAPE: - default: - return 0; - } -} +int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation); -String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) { - switch (screen_orientation) { - case DisplayServer::SCREEN_PORTRAIT: - return "portrait"; - case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - return "reverseLandscape"; - case DisplayServer::SCREEN_REVERSE_PORTRAIT: - return "reversePortrait"; - case DisplayServer::SCREEN_SENSOR_LANDSCAPE: - return "userLandscape"; - case DisplayServer::SCREEN_SENSOR_PORTRAIT: - return "userPortrait"; - case DisplayServer::SCREEN_SENSOR: - return "fullUser"; - case DisplayServer::SCREEN_LANDSCAPE: - default: - return "landscape"; - } -} +String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation); // Utility method used to create a directory. -Error create_directory(const String &p_dir) { - if (!DirAccess::exists(p_dir)) { - DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); - Error err = filesystem_da->make_dir_recursive(p_dir); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); - memdelete(filesystem_da); - } - return OK; -} +Error create_directory(const String &p_dir); // Writes p_data into a file at p_path, creating directories if necessary. // Note: this will overwrite the file at p_path if it already exists. -Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) { - String dir = p_path.get_base_dir(); - Error err = create_directory(dir); - if (err != OK) { - return err; - } - FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - fa->store_buffer(p_data.ptr(), p_data.size()); - memdelete(fa); - return OK; -} +Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data); // Writes string p_data into a file at p_path, creating directories if necessary. // Note: this will overwrite the file at p_path if it already exists. -Error store_string_at_path(const String &p_path, const String &p_data) { - String dir = p_path.get_base_dir(); - Error err = create_directory(dir); - if (err != OK) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Unable to write data into " + p_path); - } - return err; - } - FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - fa->store_string(p_data); - memdelete(fa); - return OK; -} +Error store_string_at_path(const String &p_path, const String &p_data); // Implementation of EditorExportSaveFunction. // This method will only be called as an input to export_project_files. // It is used by the export_project_files method to save all the asset files into the gradle project. // It's functionality mirrors that of the method save_apk_file. // This method will be called ONLY when custom build is enabled. -Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); - print_verbose("Saving project files from " + p_path + " into " + dst_path); - Error err = store_file_at_path(dst_path, p_data); - return err; -} +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); // Creates strings.xml files inside the gradle project for different locales. -Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) { - print_verbose("Creating strings resources for supported locales for project " + project_name); - // Stores the string into the default values directory. - String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true)); - store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string); +Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name); - // Searches the Gradle project res/ directory to find all supported locales - DirAccessRef da = DirAccess::open("res://android/build/res"); - if (!da) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Unable to open Android resources directory."); - } - return ERR_CANT_OPEN; - } - da->list_dir_begin(); - while (true) { - String file = da->get_next(); - if (file == "") { - break; - } - if (!file.begins_with("values-")) { - // NOTE: This assumes all directories that start with "values-" are for localization. - continue; - } - String locale = file.replace("values-", "").replace("-r", "_"); - String property_name = "application/config/name_" + locale; - String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml"; - if (ProjectSettings::get_singleton()->has_setting(property_name)) { - String locale_project_name = ProjectSettings::get_singleton()->get(property_name); - String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true)); - print_verbose("Storing project name for locale " + locale + " under " + locale_directory); - store_string_at_path(locale_directory, processed_xml_string); - } else { - // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch - store_string_at_path(locale_directory, processed_default_xml_string); - } - } - da->list_dir_end(); - return OK; -} +String bool_to_string(bool v); -String bool_to_string(bool v) { - return v ? "true" : "false"; -} +String _get_gles_tag(); -String _get_gles_tag() { - bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" && - !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2"); - return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : ""; -} +String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset); -String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { - String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\""; - String sizes[] = { "small", "normal", "large", "xlarge" }; - size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]); - for (size_t i = 0; i < num_sizes; i++) { - String feature_name = vformat("screen/support_%s", sizes[i]); - String feature_support = bool_to_string(p_preset->get(feature_name)); - String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support); - manifest_screen_sizes += xml_entry; - } - manifest_screen_sizes += " />\n"; - return manifest_screen_sizes; -} +String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset); -String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { - String manifest_xr_features; - bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; - if (uses_xr) { - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index == 1) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; - } else if (hand_tracking_index == 2) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n"; - } - } - return manifest_xr_features; -} +String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset); -String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) { - String package_name = p_preset->get("package/unique_name"); - String manifest_instrumentation_text = vformat( - " <instrumentation\n" - " tools:node=\"replace\"\n" - " android:name=\".GodotInstrumentation\"\n" - " android:icon=\"@mipmap/icon\"\n" - " android:label=\"@string/godot_project_name_string\"\n" - " android:targetPackage=\"%s\" />\n", - package_name); - return manifest_instrumentation_text; -} +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset); -String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { - bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; - String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); - String manifest_activity_text = vformat( - " <activity android:name=\"com.godot.game.GodotApp\" " - "tools:replace=\"android:screenOrientation\" " - "android:screenOrientation=\"%s\">\n", - orientation); - if (!uses_xr) { - manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; - } - manifest_activity_text += " </activity>\n"; - return manifest_activity_text; -} - -String _get_application_tag(const Ref<EditorExportPreset> &p_preset) { - String manifest_application_text = vformat( - " <application android:label=\"@string/godot_project_name_string\"\n" - " android:allowBackup=\"%s\"\n" - " android:icon=\"@mipmap/icon\"\n" - " android:isGame=\"%s\"\n" - " tools:replace=\"android:allowBackup,android:isGame\"\n" - " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", - bool_to_string(p_preset->get("user_data_backup/allow")), - bool_to_string(p_preset->get("package/classify_as_game"))); - - manifest_application_text += _get_activity_tag(p_preset); - manifest_application_text += " </application>\n"; - return manifest_application_text; -} +String _get_application_tag(const Ref<EditorExportPreset> &p_preset); #endif //GODOT_GRADLE_EXPORT_UTIL_H diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 8ffa4a9249..01eb1f1ec8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -842,6 +842,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } private void forceQuit() { + getActivity().finish(); System.exit(0); } diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index f21a14e84b..208626ae36 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -30,2027 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/io/file_access.h" -#include "core/io/image_loader.h" -#include "core/io/marshalls.h" -#include "core/io/resource_saver.h" -#include "core/io/zip_io.h" -#include "core/os/os.h" -#include "core/templates/safe_refcount.h" -#include "core/version.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "main/splash.gen.h" -#include "platform/iphone/logo.gen.h" -#include "platform/iphone/plugin/godot_plugin_config.h" -#include "string.h" - -#include <sys/stat.h> - -class EditorExportPlatformIOS : public EditorExportPlatform { - GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); - - int version_code; - - Ref<ImageTexture> logo; - - // Plugins - SafeFlag plugins_changed; - Thread check_for_changes_thread; - SafeFlag quit_request; - Mutex plugins_lock; - Vector<PluginConfigIOS> plugins; - - typedef Error (*FileHandler)(String p_file, void *p_userdata); - static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata); - static Error _codesign(String p_file, void *p_userdata); - void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot); - - struct IOSConfigData { - String pkg_name; - String binary_name; - String plist_content; - String architectures; - String linker_flags; - String cpp_code; - String modules_buildfile; - String modules_fileref; - String modules_buildphase; - String modules_buildgrp; - Vector<String> capabilities; - }; - struct ExportArchitecture { - String name; - bool is_default = false; - - ExportArchitecture() {} - - ExportArchitecture(String p_name, bool p_is_default) { - name = p_name; - is_default = p_is_default; - } - }; - - struct IOSExportAsset { - String exported_path; - bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource - bool should_embed = false; - }; - - String _get_additional_plist_content(); - String _get_linker_flags(); - String _get_cpp_code(); - void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug); - Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); - Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); - Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); - - Vector<ExportArchitecture> _get_supported_architectures(); - Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); - - void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); - Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); - Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); - Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); - Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); - - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Identifier is missing."); - } - return false; - } - - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); - } - return false; - } - } - - return true; - } - - static void _check_for_changes_poll_thread(void *ud) { - EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud; - - while (!ea->quit_request.is_set()) { - // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed.is_set()) { - MutexLock lock(ea->plugins_lock); - - Vector<PluginConfigIOS> loaded_plugins = get_plugins(); - - if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed.set(); - } else { - for (int i = 0; i < ea->plugins.size(); i++) { - if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) { - ea->plugins_changed.set(); - break; - } - } - } - } - - uint64_t wait = 3000000; - uint64_t time = OS::get_singleton()->get_ticks_usec(); - while (OS::get_singleton()->get_ticks_usec() - time < wait) { - OS::get_singleton()->delay_usec(300000); - - if (ea->quit_request.is_set()) { - break; - } - } - } - } - -protected: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - virtual void get_export_options(List<ExportOption> *r_options) override; - -public: - virtual String get_name() const override { return "iOS"; } - virtual String get_os_name() const override { return "iOS"; } - virtual Ref<Texture2D> get_logo() const override { return logo; } - - virtual bool should_update_export_options() override { - bool export_options_changed = plugins_changed.is_set(); - if (export_options_changed) { - // don't clear unless we're reporting true, to avoid race - plugins_changed.clear(); - } - return export_options_changed; - } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - list.push_back("ipa"); - return list; - } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("mobile"); - r_features->push_back("iOS"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformIOS(); - ~EditorExportPlatformIOS(); - - /// List the gdip files in the directory specified by the p_path parameter. - static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) { - Vector<String> dir_files; - DirAccessRef da = DirAccess::open(p_path); - if (da) { - da->list_dir_begin(); - while (true) { - String file = da->get_next(); - if (file.is_empty()) { - break; - } - - if (file == "." || file == "..") { - continue; - } - - if (da->current_is_hidden()) { - continue; - } - - if (da->current_is_dir()) { - if (p_check_directories) { - Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false); - for (int i = 0; i < directory_files.size(); ++i) { - dir_files.push_back(file.plus_file(directory_files[i])); - } - } - - continue; - } - - if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) { - dir_files.push_back(file); - } - } - da->list_dir_end(); - } - - return dir_files; - } - - static Vector<PluginConfigIOS> get_plugins() { - Vector<PluginConfigIOS> loaded_plugins; - - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins"); - - if (DirAccess::exists(plugins_dir)) { - Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true); - - if (!plugins_filenames.is_empty()) { - Ref<ConfigFile> config_file = memnew(ConfigFile); - for (int i = 0; i < plugins_filenames.size(); i++) { - PluginConfigIOS config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); - if (config.valid_config) { - loaded_plugins.push_back(config); - } else { - print_error("Invalid plugin config file " + plugins_filenames[i]); - } - } - } - } - - return loaded_plugins; - } - - static Vector<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { - Vector<PluginConfigIOS> enabled_plugins; - Vector<PluginConfigIOS> all_plugins = get_plugins(); - for (int i = 0; i < all_plugins.size(); i++) { - PluginConfigIOS plugin = all_plugins[i]; - bool enabled = p_presets->get("plugins/" + plugin.name); - if (enabled) { - enabled_plugins.push_back(plugin); - } - } - - return enabled_plugins; - } -}; - -void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - r_features->push_back("pvrtc"); - if (driver == "Vulkan") { - // FIXME: Review if this is correct. - r_features->push_back("etc2"); - } - - Vector<String> architectures = _get_preset_architectures(p_preset); - for (int i = 0; i < architectures.size(); ++i) { - r_features->push_back(architectures[i]); - } -} - -Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() { - Vector<ExportArchitecture> archs; - archs.push_back(ExportArchitecture("armv7", false)); // Disabled by default, not included in official templates. - archs.push_back(ExportArchitecture("arm64", true)); - return archs; -} - -struct LoadingScreenInfo { - const char *preset_key; - const char *export_name; - int width = 0; - int height = 0; - bool rotate = false; -}; - -static const LoadingScreenInfo loading_screen_infos[] = { - { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png", 2436, 1125, false }, - { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png", 2208, 1242, false }, - { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png", 1024, 768, false }, - { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png", 2048, 1536, false }, - - { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png", 640, 960, true }, - { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png", 640, 1136, true }, - { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png", 750, 1334, true }, - { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png", 1125, 2436, true }, - { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png", 768, 1024, true }, - { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png", 1536, 2048, true }, - { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png", 1242, 2208, true } -}; - -void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - Vector<ExportArchitecture> architectures = _get_supported_architectures(); - for (int i = 0; i < architectures.size(); ++i) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default)); - } - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - - Vector<PluginConfigIOS> found_plugins = get_plugins(); - for (int i = 0; i < found_plugins.size(); i++) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false)); - } - - Set<String> plist_keys; - - for (int i = 0; i < found_plugins.size(); i++) { - // Editable plugin plist values - PluginConfigIOS plugin = found_plugins[i]; - const String *K = nullptr; - - while ((K = plugin.plist.next(K))) { - String key = *K; - PluginConfigIOS::PlistItem item = plugin.plist[key]; - switch (item.type) { - case PluginConfigIOS::PlistItemType::STRING_INPUT: { - String preset_name = "plugins_plist/" + key; - if (!plist_keys.has(preset_name)) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), item.value)); - plist_keys.insert(preset_name); - } - } break; - default: - continue; - } - } - } - - plugins_changed.clear(); - plugins = found_plugins; - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false)); - - for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); - } -} - -void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) { - static const String export_method_string[] = { - "app-store", - "development", - "ad-hoc", - "enterprise" - }; - static const String storyboard_image_scale_mode[] = { - "center", - "scaleAspectFit", - "scaleAspectFill", - "scaleToFill" - }; - String str; - String strnew; - str.parse_utf8((const char *)pfile.ptr(), pfile.size()); - Vector<String> lines = str.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$binary") != -1) { - strnew += lines[i].replace("$binary", p_config.binary_name) + "\n"; - } else if (lines[i].find("$modules_buildfile") != -1) { - strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n"; - } else if (lines[i].find("$modules_fileref") != -1) { - strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n"; - } else if (lines[i].find("$modules_buildphase") != -1) { - strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n"; - } else if (lines[i].find("$modules_buildgrp") != -1) { - strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n"; - } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", p_config.pkg_name) + "\n"; - } else if (lines[i].find("$info") != -1) { - strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; - } else if (lines[i].find("$bundle_identifier") != -1) { - strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; - } else if (lines[i].find("$short_version") != -1) { - strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; - } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; - } else if (lines[i].find("$signature") != -1) { - strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; - } else if (lines[i].find("$copyright") != -1) { - strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; - } else if (lines[i].find("$team_id") != -1) { - strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n"; - } else if (lines[i].find("$default_build_config") != -1) { - strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n"; - } else if (lines[i].find("$export_method") != -1) { - int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release"); - strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n"; - } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) { - strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; - } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { - strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; - } else if (lines[i].find("$provisioning_profile_uuid") != -1) { - String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release"); - strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n"; - } else if (lines[i].find("$code_sign_identity_debug") != -1) { - strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n"; - } else if (lines[i].find("$code_sign_identity_release") != -1) { - strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n"; - } else if (lines[i].find("$additional_plist_content") != -1) { - strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n"; - } else if (lines[i].find("$godot_archs") != -1) { - strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n"; - } else if (lines[i].find("$linker_flags") != -1) { - strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n"; - } else if (lines[i].find("$targeted_device_family") != -1) { - String xcode_value; - switch ((int)p_preset->get("application/targeted_device_family")) { - case 0: // iPhone - xcode_value = "1"; - break; - case 1: // iPad - xcode_value = "2"; - break; - case 2: // iPhone & iPad - xcode_value = "1,2"; - break; - } - strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n"; - } else if (lines[i].find("$cpp_code") != -1) { - strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n"; - } else if (lines[i].find("$docs_in_place") != -1) { - strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n"; - } else if (lines[i].find("$docs_sharing") != -1) { - strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n"; - } else if (lines[i].find("$entitlements_push_notifications") != -1) { - bool is_on = p_preset->get("capabilities/push_notifications"); - strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n"; - } else if (lines[i].find("$required_device_capabilities") != -1) { - String capabilities; - - // I've removed armv7 as we can run on 64bit only devices - // Note that capabilities listed here are requirements for the app to be installed. - // They don't enable anything. - Vector<String> capabilities_list = p_config.capabilities; - - if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) { - capabilities_list.push_back("wifi"); - } - - for (int idx = 0; idx < capabilities_list.size(); idx++) { - capabilities += "<string>" + capabilities_list[idx] + "</string>\n"; - } - - strnew += lines[i].replace("$required_device_capabilities", capabilities); - } else if (lines[i].find("$interface_orientations") != -1) { - String orientations; - const DisplayServer::ScreenOrientation screen_orientation = - DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))); - - switch (screen_orientation) { - case DisplayServer::SCREEN_LANDSCAPE: - orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; - break; - case DisplayServer::SCREEN_PORTRAIT: - orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; - break; - case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; - break; - case DisplayServer::SCREEN_REVERSE_PORTRAIT: - orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; - break; - case DisplayServer::SCREEN_SENSOR_LANDSCAPE: - // Allow both landscape orientations depending on sensor direction. - orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; - orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; - break; - case DisplayServer::SCREEN_SENSOR_PORTRAIT: - // Allow both portrait orientations depending on sensor direction. - orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; - orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; - break; - case DisplayServer::SCREEN_SENSOR: - // Allow all screen orientations depending on sensor direction. - orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; - orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; - orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; - orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; - break; - } - - strnew += lines[i].replace("$interface_orientations", orientations); - } else if (lines[i].find("$camera_usage_description") != -1) { - String description = p_preset->get("privacy/camera_usage_description"); - strnew += lines[i].replace("$camera_usage_description", description) + "\n"; - } else if (lines[i].find("$microphone_usage_description") != -1) { - String description = p_preset->get("privacy/microphone_usage_description"); - strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; - } else if (lines[i].find("$photolibrary_usage_description") != -1) { - String description = p_preset->get("privacy/photolibrary_usage_description"); - strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n"; - } else if (lines[i].find("$plist_launch_screen_name") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>" : ""; - strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };" : ""; - strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */," : ""; - strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */," : ""; - strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : ""; - strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n"; - } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;"; - strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n"; - } else if (lines[i].find("$launch_screen_image_mode") != -1) { - int image_scale_mode = p_preset->get("storyboard/image_scale_mode"); - String value; - - switch (image_scale_mode) { - case 0: { - String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - // If custom logo is not specified, Godot does not scale default one, so we should do the same. - value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center"; - } break; - default: { - value = storyboard_image_scale_mode[image_scale_mode - 1]; - } - } - - strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; - } else if (lines[i].find("$launch_screen_background_color") != -1) { - bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); - Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; - - Dictionary value_dictionary; - value_dictionary["red"] = color.r; - value_dictionary["green"] = color.g; - value_dictionary["blue"] = color.b; - value_dictionary["alpha"] = color.a; - String value = value_format.format(value_dictionary, "$_"); - - strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; - } else { - strnew += lines[i] + "\n"; - } - } - - // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero... - // should apply the same fix in our OSX export. - CharString cs = strnew.utf8(); - pfile.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - pfile.write[i] = cs[i]; - } -} - -String EditorExportPlatformIOS::_get_additional_plist_content() { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - String result; - for (int i = 0; i < export_plugins.size(); ++i) { - result += export_plugins[i]->get_ios_plist_content(); - } - return result; -} - -String EditorExportPlatformIOS::_get_linker_flags() { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - String result; - for (int i = 0; i < export_plugins.size(); ++i) { - String flags = export_plugins[i]->get_ios_linker_flags(); - if (flags.length() == 0) { - continue; - } - if (result.length() > 0) { - result += ' '; - } - result += flags; - } - // the flags will be enclosed in quotes, so need to escape them - return result.replace("\"", "\\\""); -} - -String EditorExportPlatformIOS::_get_cpp_code() { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - String result; - for (int i = 0; i < export_plugins.size(); ++i) { - result += export_plugins[i]->get_ios_cpp_code(); - } - return result; -} - -void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) { - ERR_FAIL_COND(p_dst.is_null()); - ERR_FAIL_COND(p_src.is_null()); - - int sw = p_rot ? p_src->get_height() : p_src->get_width(); - int sh = p_rot ? p_src->get_width() : p_src->get_height(); - - int x_pos = (p_dst->get_width() - sw) / 2; - int y_pos = (p_dst->get_height() - sh) / 2; - - int xs = (x_pos >= 0) ? 0 : -x_pos; - int ys = (y_pos >= 0) ? 0 : -y_pos; - - if (sw + x_pos > p_dst->get_width()) { - sw = p_dst->get_width() - x_pos; - } - if (sh + y_pos > p_dst->get_height()) { - sh = p_dst->get_height() - y_pos; - } - - for (int y = ys; y < sh; y++) { - for (int x = xs; x < sw; x++) { - Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y); - Color dc = p_dst->get_pixel(x_pos + x, y_pos + y); - dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); - dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); - dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); - dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); - p_dst->set_pixel(x_pos + x, y_pos + y, dc); - } - } -} - -struct IconInfo { - const char *preset_key; - const char *idiom; - const char *export_name; - const char *actual_size_side; - const char *scale; - const char *unscaled_size; - bool is_required = false; -}; - -static const IconInfo icon_infos[] = { - { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true }, - { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true }, - - { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true }, - { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, - - { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, - - { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, - - { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, - - { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, - - { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, - { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } -}; - -Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { - String json_description = "{\"images\":["; - String sizes; - - DirAccess *da = DirAccess::open(p_iconset_dir); - ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - IconInfo info = icon_infos[i]; - int side_size = String(info.actual_size_side).to_int(); - String icon_path = p_preset->get(info.preset_key); - if (icon_path.length() == 0) { - if ((bool)p_preset->get("icons/generate_missing")) { - // Resize main app icon - icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); - return ERR_UNCONFIGURED; - } - img->resize(side_size, side_size); - err = img->save_png(p_iconset_dir + info.export_name); - if (err) { - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); - return err; - } - } else { - if (info.is_required) { - String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset."; - ERR_PRINT(err_str); - return ERR_UNCONFIGURED; - } else { - String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset."; - WARN_PRINT(err_str); - } - continue; - } - } else { - // Load custom icon - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); - return ERR_UNCONFIGURED; - } - if (img->get_width() != side_size || img->get_height() != side_size) { - ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'."); - return ERR_UNCONFIGURED; - } - - err = da->copy(icon_path, p_iconset_dir + info.export_name); - if (err) { - memdelete(da); - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); - return err; - } - } - sizes += String(info.actual_size_side) + "\n"; - if (i > 0) { - json_description += ","; - } - json_description += String("{"); - json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; - json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; - json_description += String("\"scale\":") + "\"" + info.scale + "\","; - json_description += String("\"filename\":") + "\"" + info.export_name + "\""; - json_description += String("}"); - } - json_description += "]}"; - memdelete(da); - - FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); - ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE); - CharString json_utf8 = json_description.utf8(); - json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length()); - memdelete(json_file); - - FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE); - ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE); - CharString sizes_utf8 = sizes.utf8(); - sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length()); - memdelete(sizes_file); - - return OK; -} - -Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { - const String custom_launch_image_2x = p_preset->get("storyboard/custom_image@2x"); - const String custom_launch_image_3x = p_preset->get("storyboard/custom_image@3x"); - - if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) { - Ref<Image> image; - String image_path = p_dest_dir.plus_file("splash@2x.png"); - image.instantiate(); - Error err = image->load(custom_launch_image_2x); - - if (err) { - image.unref(); - return err; - } - - if (image->save_png(image_path) != OK) { - return ERR_FILE_CANT_WRITE; - } - - image.unref(); - image_path = p_dest_dir.plus_file("splash@3x.png"); - image.instantiate(); - err = image->load(custom_launch_image_3x); - - if (err) { - image.unref(); - return err; - } - - if (image->save_png(image_path) != OK) { - return ERR_FILE_CANT_WRITE; - } - } else { - Ref<Image> splash; - - const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - - if (!splash_path.is_empty()) { - splash.instantiate(); - const Error err = splash->load(splash_path); - if (err) { - splash.unref(); - } - } - - if (splash.is_null()) { - splash = Ref<Image>(memnew(Image(boot_splash_png))); - } - - // Using same image for both @2x and @3x - // because Godot's own boot logo uses single image for all resolutions. - // Also not using @1x image, because devices using this image variant - // are not supported by iOS 9, which is minimal target. - const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png"); - const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png"); - - if (splash->save_png(splash_png_path_2x) != OK) { - return ERR_FILE_CANT_WRITE; - } - - if (splash->save_png(splash_png_path_3x) != OK) { - return ERR_FILE_CANT_WRITE; - } - } - - return OK; -} - -Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { - DirAccess *da = DirAccess::open(p_dest_dir); - ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'."); - - for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { - LoadingScreenInfo info = loading_screen_infos[i]; - String loading_screen_file = p_preset->get(info.preset_key); - if (loading_screen_file.size() > 0) { - // Load custom loading screens - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(loading_screen_file, img); - if (err != OK) { - ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); - return ERR_UNCONFIGURED; - } - if (img->get_width() != info.width || img->get_height() != info.height) { - ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); - return ERR_UNCONFIGURED; - } - err = da->copy(loading_screen_file, p_dest_dir + info.export_name); - if (err) { - memdelete(da); - String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'."; - ERR_PRINT(err_str.utf8().get_data()); - return err; - } - } else if ((bool)p_preset->get("launch_screens/generate_missing")) { - // Generate loading screen from the splash screen - Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); - img->fill(boot_bg_color); - - Ref<Image> img_bs; - - if (boot_logo_path.length() > 0) { - img_bs = Ref<Image>(memnew(Image)); - ImageLoader::load_image(boot_logo_path, img_bs); - } - if (!img_bs.is_valid()) { - img_bs = Ref<Image>(memnew(Image(boot_splash_png))); - } - if (img_bs.is_valid()) { - float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height(); - if (info.rotate) { - if (boot_logo_scale) { - if (info.width * aspect_ratio <= info.height) { - img_bs->resize(info.width * aspect_ratio, info.width); - } else { - img_bs->resize(info.height, info.height / aspect_ratio); - } - } - } else { - if (boot_logo_scale) { - if (info.height * aspect_ratio <= info.width) { - img_bs->resize(info.height * aspect_ratio, info.height); - } else { - img_bs->resize(info.width, info.width / aspect_ratio); - } - } - } - _blend_and_rotate(img, img_bs, info.rotate); - } - Error err = img->save_png(p_dest_dir + info.export_name); - if (err) { - String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen."; - WARN_PRINT(err_str.utf8().get_data()); - } - } else { - String err_str = String("No loading screen (") + info.preset_key + ") specified."; - WARN_PRINT(err_str.utf8().get_data()); - } - } - memdelete(da); - - return OK; -} - -Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) { - Vector<String> dirs; - String path; - String current_dir = p_da->get_current_dir(); - p_da->list_dir_begin(); - while ((path = p_da->get_next()).length() != 0) { - if (p_da->current_is_dir()) { - if (path != "." && path != "..") { - dirs.push_back(path); - } - } else { - Error err = p_handler(current_dir.plus_file(path), p_userdata); - if (err) { - p_da->list_dir_end(); - return err; - } - } - } - p_da->list_dir_end(); - - for (int i = 0; i < dirs.size(); ++i) { - String dir = dirs[i]; - p_da->change_dir(dir); - Error err = _walk_dir_recursive(p_da, p_handler, p_userdata); - p_da->change_dir(".."); - if (err) { - return err; - } - } - - return OK; -} - -struct CodesignData { - const Ref<EditorExportPreset> &preset; - bool debug = false; - - CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) : - preset(p_preset), - debug(p_debug) { - } -}; - -Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) { - if (p_file.ends_with(".dylib")) { - CodesignData *data = (CodesignData *)p_userdata; - print_line(String("Signing ") + p_file); - List<String> codesign_args; - codesign_args.push_back("-f"); - codesign_args.push_back("-s"); - codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release")); - codesign_args.push_back(p_file); - return OS::get_singleton()->execute("codesign", codesign_args); - } - return OK; -} - -struct PbxId { -private: - static char _hex_char(uint8_t four_bits) { - if (four_bits < 10) { - return ('0' + four_bits); - } - return 'A' + (four_bits - 10); - } - - static String _hex_pad(uint32_t num) { - Vector<char> ret; - ret.resize(sizeof(num) * 2); - for (uint64_t i = 0; i < sizeof(num) * 2; ++i) { - uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; - ret.write[i] = _hex_char(four_bits); - } - return String::utf8(ret.ptr(), ret.size()); - } - -public: - uint32_t high_bits; - uint32_t mid_bits; - uint32_t low_bits; - - String str() const { - return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits); - } - - PbxId &operator++() { - low_bits++; - if (!low_bits) { - mid_bits++; - if (!mid_bits) { - high_bits++; - } - } - - return *this; - } -}; - -struct ExportLibsData { - Vector<String> lib_paths; - String dest_dir; -}; - -void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { - // that is just a random number, we just need Godot IDs not to clash with - // existing IDs in the project. - PbxId current_id = { 0x58938401, 0, 0 }; - String pbx_files; - String pbx_frameworks_build; - String pbx_frameworks_refs; - String pbx_resources_build; - String pbx_resources_refs; - String pbx_embeded_frameworks; - - const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") + - "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"<group>\"; };\n"; - - for (int i = 0; i < p_additional_assets.size(); ++i) { - String additional_asset_info_format = file_info_format; - - String build_id = (++current_id).str(); - String ref_id = (++current_id).str(); - String framework_id = ""; - - const IOSExportAsset &asset = p_additional_assets[i]; - - String type; - if (asset.exported_path.ends_with(".framework")) { - if (asset.should_embed) { - additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; - framework_id = (++current_id).str(); - pbx_embeded_frameworks += framework_id + ",\n"; - } - - type = "wrapper.framework"; - } else if (asset.exported_path.ends_with(".xcframework")) { - if (asset.should_embed) { - additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; - framework_id = (++current_id).str(); - pbx_embeded_frameworks += framework_id + ",\n"; - } - - type = "wrapper.xcframework"; - } else if (asset.exported_path.ends_with(".dylib")) { - type = "compiled.mach-o.dylib"; - } else if (asset.exported_path.ends_with(".a")) { - type = "archive.ar"; - } else { - type = "file"; - } - - String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build; - String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs; - - if (pbx_build.length() > 0) { - pbx_build += ",\n"; - pbx_refs += ",\n"; - } - pbx_build += build_id; - pbx_refs += ref_id; - - Dictionary format_dict; - format_dict["build_id"] = build_id; - format_dict["ref_id"] = ref_id; - format_dict["name"] = asset.exported_path.get_file(); - format_dict["file_path"] = asset.exported_path; - format_dict["file_type"] = type; - if (framework_id.length() > 0) { - format_dict["framework_id"] = framework_id; - } - pbx_files += additional_asset_info_format.format(format_dict, "$_"); - } - - // Note, frameworks like gamekit are always included in our project.pbxprof file - // even if turned off in capabilities. - - String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size()); - str = str.replace("$additional_pbx_files", pbx_files); - str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build); - str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs); - str = str.replace("$additional_pbx_resources_build", pbx_resources_build); - str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs); - str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks); - - CharString cs = str.utf8(); - p_project_data.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - p_project_data.write[i] = cs[i]; - } -} - -Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { - DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'."); - - String binary_name = p_out_dir.get_file().get_basename(); - - DirAccess *da = DirAccess::create_for_path(p_asset); - if (!da) { - memdelete(filesystem_da); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + "."); - } - bool file_exists = da->file_exists(p_asset); - bool dir_exists = da->dir_exists(p_asset); - if (!file_exists && !dir_exists) { - memdelete(da); - memdelete(filesystem_da); - return ERR_FILE_NOT_FOUND; - } - - String base_dir = p_asset.get_base_dir().replace("res://", ""); - String destination_dir; - String destination; - String asset_path; - - bool create_framework = false; - - if (p_is_framework && p_asset.ends_with(".dylib")) { - // For iOS we need to turn .dylib into .framework - // to be able to send application to AppStore - asset_path = String("dylibs").plus_file(base_dir); - - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_basename().get_file(); - } else { - file_name = *p_custom_file_name; - } - - String framework_name = file_name + ".framework"; - - asset_path = asset_path.plus_file(framework_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir.plus_file(file_name); - create_framework = true; - } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) { - asset_path = String("dylibs").plus_file(base_dir); - - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_file(); - } else { - file_name = *p_custom_file_name; - } - - asset_path = asset_path.plus_file(file_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir; - } else { - asset_path = base_dir; - - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_file(); - } else { - file_name = *p_custom_file_name; - } - - destination_dir = p_out_dir.plus_file(asset_path); - asset_path = asset_path.plus_file(file_name); - destination = p_out_dir.plus_file(asset_path); - } - - if (!filesystem_da->dir_exists(destination_dir)) { - Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); - if (make_dir_err) { - memdelete(da); - memdelete(filesystem_da); - return make_dir_err; - } - } - - Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination); - memdelete(da); - if (err) { - memdelete(filesystem_da); - return err; - } - IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; - r_exported_assets.push_back(exported_asset); - - if (create_framework) { - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_basename().get_file(); - } else { - file_name = *p_custom_file_name; - } - - String framework_name = file_name + ".framework"; - - // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib - { - List<String> install_name_args; - install_name_args.push_back("-id"); - install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name)); - install_name_args.push_back(destination); - - OS::get_singleton()->execute("install_name_tool", install_name_args); - } - - // Creating Info.plist - { - String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" - "<plist version=\"1.0\">\n" - "<dict>\n" - "<key>CFBundleShortVersionString</key>\n" - "<string>1.0</string>\n" - "<key>CFBundleIdentifier</key>\n" - "<string>com.gdnative.framework.$name</string>\n" - "<key>CFBundleName</key>\n" - "<string>$name</string>\n" - "<key>CFBundleExecutable</key>\n" - "<string>$name</string>\n" - "<key>DTPlatformName</key>\n" - "<string>iphoneos</string>\n" - "<key>CFBundleInfoDictionaryVersion</key>\n" - "<string>6.0</string>\n" - "<key>CFBundleVersion</key>\n" - "<string>1</string>\n" - "<key>CFBundlePackageType</key>\n" - "<string>FMWK</string>\n" - "<key>MinimumOSVersion</key>\n" - "<string>10.0</string>\n" - "</dict>\n" - "</plist>"; - - String info_plist = info_plist_format.replace("$name", file_name); - - FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); - if (f) { - f->store_string(info_plist); - f->close(); - memdelete(f); - } - } - } - - memdelete(filesystem_da); - - return OK; -} - -Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { - for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { - String asset = p_assets[f_idx]; - if (!asset.begins_with("res://")) { - // either SDK-builtin or already a part of the export template - IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; - r_exported_assets.push_back(exported_asset); - } else { - Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); - ERR_FAIL_COND_V(err, err); - } - } - - return OK; -} - -Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - for (int i = 0; i < export_plugins.size(); i++) { - Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks(); - Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks(); - err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); - for (int j = 0; j < project_static_libs.size(); j++) { - project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project - } - err = _export_additional_assets(p_out_dir, project_static_libs, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files(); - err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - } - - Vector<String> library_paths; - for (int i = 0; i < p_libraries.size(); ++i) { - library_paths.push_back(p_libraries[i].path); - } - Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - return OK; -} - -Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) { - Vector<ExportArchitecture> all_archs = _get_supported_architectures(); - Vector<String> enabled_archs; - for (int i = 0; i < all_archs.size(); ++i) { - bool is_enabled = p_preset->get("architectures/" + all_archs[i].name); - if (is_enabled) { - enabled_archs.push_back(all_archs[i].name); - } - } - return enabled_archs; -} - -Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) { - String plugin_definition_cpp_code; - String plugin_initialization_cpp_code; - String plugin_deinitialization_cpp_code; - - Vector<String> plugin_linked_dependencies; - Vector<String> plugin_embedded_dependencies; - Vector<String> plugin_files; - - Vector<PluginConfigIOS> enabled_plugins = get_enabled_plugins(p_preset); - - Vector<String> added_linked_dependenciy_names; - Vector<String> added_embedded_dependenciy_names; - HashMap<String, String> plist_values; - - Set<String> plugin_linker_flags; - - Error err; - - for (int i = 0; i < enabled_plugins.size(); i++) { - PluginConfigIOS plugin = enabled_plugins[i]; - - // Export plugin binary. - String plugin_main_binary = get_plugin_main_binary(plugin, p_debug); - String plugin_binary_result_file = plugin.binary.get_file(); - // We shouldn't embed .xcframework that contains static libraries. - // Static libraries are not embedded anyway. - err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); - - ERR_FAIL_COND_V(err, err); - - // Adding dependencies. - // Use separate container for names to check for duplicates. - for (int j = 0; j < plugin.linked_dependencies.size(); j++) { - String dependency = plugin.linked_dependencies[j]; - String name = dependency.get_file(); - - if (added_linked_dependenciy_names.has(name)) { - continue; - } - - added_linked_dependenciy_names.push_back(name); - plugin_linked_dependencies.push_back(dependency); - } - - for (int j = 0; j < plugin.system_dependencies.size(); j++) { - String dependency = plugin.system_dependencies[j]; - String name = dependency.get_file(); - - if (added_linked_dependenciy_names.has(name)) { - continue; - } - - added_linked_dependenciy_names.push_back(name); - plugin_linked_dependencies.push_back(dependency); - } - - for (int j = 0; j < plugin.embedded_dependencies.size(); j++) { - String dependency = plugin.embedded_dependencies[j]; - String name = dependency.get_file(); - - if (added_embedded_dependenciy_names.has(name)) { - continue; - } - - added_embedded_dependenciy_names.push_back(name); - plugin_embedded_dependencies.push_back(dependency); - } - - plugin_files.append_array(plugin.files_to_copy); - - // Capabilities - // Also checking for duplicates. - for (int j = 0; j < plugin.capabilities.size(); j++) { - String capability = plugin.capabilities[j]; - - if (p_config_data.capabilities.has(capability)) { - continue; - } - - p_config_data.capabilities.push_back(capability); - } - - // Linker flags - // Checking duplicates - for (int j = 0; j < plugin.linker_flags.size(); j++) { - String linker_flag = plugin.linker_flags[j]; - plugin_linker_flags.insert(linker_flag); - } - - // Plist - // Using hash map container to remove duplicates - const String *K = nullptr; - - while ((K = plugin.plist.next(K))) { - String key = *K; - PluginConfigIOS::PlistItem item = plugin.plist[key]; - - String value; - - switch (item.type) { - case PluginConfigIOS::PlistItemType::STRING_INPUT: { - String preset_name = "plugins_plist/" + key; - String input_value = p_preset->get(preset_name); - value = "<string>" + input_value + "</string>"; - } break; - default: - value = item.value; - break; - } - - if (key.is_empty() || value.is_empty()) { - continue; - } - - String plist_key = "<key>" + key + "</key>"; - - plist_values[plist_key] = value; - } - - // CPP Code - String definition_comment = "// Plugin: " + plugin.name + "\n"; - String initialization_method = plugin.initialization_method + "();\n"; - String deinitialization_method = plugin.deinitialization_method + "();\n"; - - plugin_definition_cpp_code += definition_comment + - "extern void " + initialization_method + - "extern void " + deinitialization_method + "\n"; - - plugin_initialization_cpp_code += "\t" + initialization_method; - plugin_deinitialization_cpp_code += "\t" + deinitialization_method; - } - - // Updating `Info.plist` - { - const String *K = nullptr; - while ((K = plist_values.next(K))) { - String key = *K; - String value = plist_values[key]; - - if (key.is_empty() || value.is_empty()) { - continue; - } - - p_config_data.plist_content += key + value + "\n"; - } - } - - // Export files - { - // Export linked plugin dependency - err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - // Export embedded plugin dependency - err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - // Export plugin files - err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - } - - // Update CPP - { - Dictionary plugin_format; - plugin_format["definition"] = plugin_definition_cpp_code; - plugin_format["initialization"] = plugin_initialization_cpp_code; - plugin_format["deinitialization"] = plugin_deinitialization_cpp_code; - - String plugin_cpp_code = "\n// Godot Plugins\n" - "void godot_ios_plugins_initialize();\n" - "void godot_ios_plugins_deinitialize();\n" - "// Exported Plugins\n\n" - "$definition" - "// Use Plugins\n" - "void godot_ios_plugins_initialize() {\n" - "$initialization" - "}\n\n" - "void godot_ios_plugins_deinitialize() {\n" - "$deinitialization" - "}\n"; - - p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_"); - } - - // Update Linker Flag Values - { - String result_linker_flags = " "; - for (Set<String>::Element *E = plugin_linker_flags.front(); E; E = E->next()) { - const String &flag = E->get(); - - if (flag.length() == 0) { - continue; - } - - if (result_linker_flags.length() > 0) { - result_linker_flags += ' '; - } - - result_linker_flags += flag; - } - result_linker_flags = result_linker_flags.replace("\"", "\\\""); - p_config_data.linker_flags += result_linker_flags; - } - - return OK; -} - -Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_pkg_name; - String dest_dir = p_path.get_base_dir() + "/"; - String binary_name = p_path.get_file().get_basename(); - - EditorProgress ep("export", "Exporting for iOS", 5, true); - - String team_id = p_preset->get("application/app_store_team_id"); - ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project."); - - if (p_debug) { - src_pkg_name = p_preset->get("custom_template/debug"); - } else { - src_pkg_name = p_preset->get("custom_template/release"); - } - - if (src_pkg_name == "") { - String err; - src_pkg_name = find_export_template("iphone.zip", &err); - if (src_pkg_name == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(dest_dir)) { - return ERR_FILE_BAD_PATH; - } - - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da) { - String current_dir = da->get_current_dir(); - - // remove leftovers from last export so they don't interfere - // in case some files are no longer needed - if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) { - da->erase_contents_recursive(); - } - if (da->change_dir(dest_dir + binary_name) == OK) { - da->erase_contents_recursive(); - } - - da->change_dir(current_dir); - - if (!da->dir_exists(dest_dir + binary_name)) { - Error err = da->make_dir(dest_dir + binary_name); - if (err) { - memdelete(da); - return err; - } - } - memdelete(da); - } - - if (ep.step("Making .pck", 0)) { - return ERR_SKIP; - } - String pack_path = dest_dir + binary_name + ".pck"; - Vector<SharedObject> libraries; - Error err = save_pack(p_preset, pack_path, &libraries); - if (err) { - return err; - } - - if (ep.step("Extracting and configuring Xcode project", 1)) { - return ERR_SKIP; - } - - String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".xcframework"; - - print_line("Static framework: " + library_to_use); - String pkg_name; - if (p_preset->get("application/name") != "") { - pkg_name = p_preset->get("application/name"); // app_name - } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - pkg_name = "Unnamed"; - } - - bool found_library = false; - int total_size = 0; - - const String project_file = "godot_ios.xcodeproj/project.pbxproj"; - Set<String> files_to_parse; - files_to_parse.insert("godot_ios/godot_ios-Info.plist"); - files_to_parse.insert(project_file); - files_to_parse.insert("godot_ios/export_options.plist"); - files_to_parse.insert("godot_ios/dummy.cpp"); - files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata"); - files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); - files_to_parse.insert("godot_ios/godot_ios.entitlements"); - files_to_parse.insert("godot_ios/Launch Screen.storyboard"); - - IOSConfigData config_data = { - pkg_name, - binary_name, - _get_additional_plist_content(), - String(" ").join(_get_preset_architectures(p_preset)), - _get_linker_flags(), - _get_cpp_code(), - "", - "", - "", - "", - Vector<String>() - }; - - Vector<IOSExportAsset> assets; - - DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); - ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); - - print_line("Unzipping..."); - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); - if (!src_pkg_zip) { - EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name); - return ERR_CANT_OPEN; - } - - err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug); - ERR_FAIL_COND_V(err, err); - - //export rest of the files - int ret = unzGoToFirstFile(src_pkg_zip); - Vector<uint8_t> project_file_data; - while (ret == UNZ_OK) { -#if defined(OSX_ENABLED) || defined(X11_ENABLED) - bool is_execute = false; -#endif - - //get filename - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); - - String file = fname; - - print_line("READ: " + file); - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - //read - unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); - unzCloseCurrentFile(src_pkg_zip); - - //write - - file = file.replace_first("iphone/", ""); - - if (files_to_parse.has(file)) { - _fix_config_file(p_preset, data, config_data, p_debug); - } else if (file.begins_with("libgodot.iphone")) { - if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) { - ret = unzGoToNextFile(src_pkg_zip); - continue; //ignore! - } - found_library = true; -#if defined(OSX_ENABLED) || defined(X11_ENABLED) - is_execute = true; -#endif - file = file.replace(library_to_use, binary_name + ".xcframework"); - } - - if (file == project_file) { - project_file_data = data; - } - - ///@TODO need to parse logo files - - if (data.size() > 0) { - file = file.replace("godot_ios", binary_name); - - print_line("ADDING: " + file + " size: " + itos(data.size())); - total_size += data.size(); - - /* write it into our folder structure */ - file = dest_dir + file; - - /* make sure this folder exists */ - String dir_name = file.get_base_dir(); - if (!tmp_app_path->dir_exists(dir_name)) { - print_line("Creating " + dir_name); - Error dir_err = tmp_app_path->make_dir_recursive(dir_name); - if (dir_err) { - ERR_PRINT("Can't create '" + dir_name + "'."); - unzClose(src_pkg_zip); - memdelete(tmp_app_path); - return ERR_CANT_CREATE; - } - } - - /* write the file */ - FileAccess *f = FileAccess::open(file, FileAccess::WRITE); - if (!f) { - ERR_PRINT("Can't write '" + file + "'."); - unzClose(src_pkg_zip); - memdelete(tmp_app_path); - return ERR_CANT_CREATE; - }; - f->store_buffer(data.ptr(), data.size()); - f->close(); - memdelete(f); - -#if defined(OSX_ENABLED) || defined(X11_ENABLED) - if (is_execute) { - // we need execute rights on this file - chmod(file.utf8().get_data(), 0755); - } -#endif - } - - ret = unzGoToNextFile(src_pkg_zip); - } - - /* we're done with our source zip */ - unzClose(src_pkg_zip); - - if (!found_library) { - ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive."); - memdelete(tmp_app_path); - return ERR_FILE_NOT_FOUND; - } - - // Copy project static libs to the project - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - for (int i = 0; i < export_plugins.size(); i++) { - Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); - for (int j = 0; j < project_static_libs.size(); j++) { - const String &static_lib_path = project_static_libs[j]; - String dest_lib_file_path = dest_dir + static_lib_path.get_file(); - Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path); - if (lib_copy_err != OK) { - ERR_PRINT("Can't copy '" + static_lib_path + "'."); - memdelete(tmp_app_path); - return lib_copy_err; - } - } - } - - String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; - err = OK; - if (!tmp_app_path->dir_exists(iconset_dir)) { - err = tmp_app_path->make_dir_recursive(iconset_dir); - } - memdelete(tmp_app_path); - if (err) { - return err; - } - - err = _export_icons(p_preset, iconset_dir); - if (err) { - return err; - } - - bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard"); - - String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/"; - String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/"; - - DirAccess *launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - - if (!launch_screen_da) { - return ERR_CANT_CREATE; - } - - if (use_storyboard) { - print_line("Using Launch Storyboard"); - - if (launch_screen_da->change_dir(launch_image_path) == OK) { - launch_screen_da->erase_contents_recursive(); - launch_screen_da->remove(launch_image_path); - } - - err = _export_loading_screen_file(p_preset, splash_image_path); - } else { - print_line("Using Launch Images"); - - const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard"; - - launch_screen_da->remove(launch_screen_path); - - if (launch_screen_da->change_dir(splash_image_path) == OK) { - launch_screen_da->erase_contents_recursive(); - launch_screen_da->remove(splash_image_path); - } - - err = _export_loading_screen_images(p_preset, launch_image_path); - } - - memdelete(launch_screen_da); - - if (err) { - return err; - } - - print_line("Exporting additional assets"); - _export_additional_assets(dest_dir + binary_name, libraries, assets); - _add_assets_to_project(p_preset, project_file_data, assets); - String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; - FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE); - if (!f) { - ERR_PRINT("Can't write '" + project_file_name + "'."); - return ERR_CANT_CREATE; - }; - f->store_buffer(project_file_data.ptr(), project_file_data.size()); - f->close(); - memdelete(f); - -#ifdef OSX_ENABLED - if (ep.step("Code-signing dylibs", 2)) { - return ERR_SKIP; - } - DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); - ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); - CodesignData codesign_data(p_preset, p_debug); - err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); - memdelete(dylibs_dir); - ERR_FAIL_COND_V(err, err); - - if (ep.step("Making .xcarchive", 3)) { - return ERR_SKIP; - } - String archive_path = p_path.get_basename() + ".xcarchive"; - List<String> archive_args; - archive_args.push_back("-project"); - archive_args.push_back(dest_dir + binary_name + ".xcodeproj"); - archive_args.push_back("-scheme"); - archive_args.push_back(binary_name); - archive_args.push_back("-sdk"); - archive_args.push_back("iphoneos"); - archive_args.push_back("-configuration"); - archive_args.push_back(p_debug ? "Debug" : "Release"); - archive_args.push_back("-destination"); - archive_args.push_back("generic/platform=iOS"); - archive_args.push_back("archive"); - archive_args.push_back("-archivePath"); - archive_args.push_back(archive_path); - err = OS::get_singleton()->execute("xcodebuild", archive_args); - ERR_FAIL_COND_V(err, err); - - if (ep.step("Making .ipa", 4)) { - return ERR_SKIP; - } - List<String> export_args; - export_args.push_back("-exportArchive"); - export_args.push_back("-archivePath"); - export_args.push_back(archive_path); - export_args.push_back("-exportOptionsPlist"); - export_args.push_back(dest_dir + binary_name + "/export_options.plist"); - export_args.push_back("-allowProvisioningUpdates"); - export_args.push_back("-exportPath"); - export_args.push_back(dest_dir); - err = OS::get_singleton()->execute("xcodebuild", export_args); - ERR_FAIL_COND_V(err, err); -#else - print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package."); -#endif - - return OK; -} - -bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - bool dvalid = exists_export_template("iphone.zip", &err); - bool rvalid = dvalid; // Both in the same ZIP. - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - // Validate the rest of the configuration. - - String team_id = p_preset->get("application/app_store_team_id"); - if (team_id.length() == 0) { - err += TTR("App Store Team ID not specified - cannot configure the project.") + "\n"; - valid = false; - } - - String identifier = p_preset->get("application/bundle_identifier"); - String pn_err; - if (!is_package_name_valid(identifier, &pn_err)) { - err += TTR("Invalid Identifier:") + " " + pn_err + "\n"; - valid = false; - } - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - IconInfo info = icon_infos[i]; - String icon_path = p_preset->get(info.preset_key); - if (icon_path.length() == 0) { - if (info.is_required) { - err += TTR("Required icon is not specified in the preset.") + "\n"; - valid = false; - } - break; - } - } - - String etc_error = test_etc2_or_pvrtc(); - if (etc_error != String()) { - valid = false; - err += etc_error; - } - - if (!err.is_empty()) { - r_error = err; - } - - return valid; -} - -EditorExportPlatformIOS::EditorExportPlatformIOS() { - Ref<Image> img = memnew(Image(_iphone_logo)); - logo.instantiate(); - logo->create_from_image(img); - - plugins_changed.set(); - - check_for_changes_thread.start(_check_for_changes_poll_thread, this); -} - -EditorExportPlatformIOS::~EditorExportPlatformIOS() { - quit_request.set(); - check_for_changes_thread.wait_to_finish(); -} +#include "export_plugin.h" void register_iphone_exporter() { Ref<EditorExportPlatformIOS> platform; diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp new file mode 100644 index 0000000000..69a8203e9f --- /dev/null +++ b/platform/iphone/export/export_plugin.cpp @@ -0,0 +1,1792 @@ +/*************************************************************************/ +/* export_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 "export_plugin.h" + +void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + r_features->push_back("pvrtc"); + if (driver == "Vulkan") { + // FIXME: Review if this is correct. + r_features->push_back("etc2"); + } + + Vector<String> architectures = _get_preset_architectures(p_preset); + for (int i = 0; i < architectures.size(); ++i) { + r_features->push_back(architectures[i]); + } +} + +Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() { + Vector<ExportArchitecture> archs; + archs.push_back(ExportArchitecture("armv7", false)); // Disabled by default, not included in official templates. + archs.push_back(ExportArchitecture("arm64", true)); + return archs; +} + +struct LoadingScreenInfo { + const char *preset_key; + const char *export_name; + int width = 0; + int height = 0; + bool rotate = false; +}; + +static const LoadingScreenInfo loading_screen_infos[] = { + { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png", 2436, 1125, false }, + { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png", 2208, 1242, false }, + { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png", 1024, 768, false }, + { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png", 2048, 1536, false }, + + { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png", 640, 960, true }, + { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png", 640, 1136, true }, + { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png", 750, 1334, true }, + { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png", 1125, 2436, true }, + { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png", 768, 1024, true }, + { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png", 1536, 2048, true }, + { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png", 1242, 2208, true } +}; + +void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + Vector<ExportArchitecture> architectures = _get_supported_architectures(); + for (int i = 0; i < architectures.size(); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + + Vector<PluginConfigIOS> found_plugins = get_plugins(); + for (int i = 0; i < found_plugins.size(); i++) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false)); + } + + Set<String> plist_keys; + + for (int i = 0; i < found_plugins.size(); i++) { + // Editable plugin plist values + PluginConfigIOS plugin = found_plugins[i]; + const String *K = nullptr; + + while ((K = plugin.plist.next(K))) { + String key = *K; + PluginConfigIOS::PlistItem item = plugin.plist[key]; + switch (item.type) { + case PluginConfigIOS::PlistItemType::STRING_INPUT: { + String preset_name = "plugins_plist/" + key; + if (!plist_keys.has(preset_name)) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), item.value)); + plist_keys.insert(preset_name); + } + } break; + default: + continue; + } + } + } + + plugins_changed.clear(); + plugins = found_plugins; + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false)); + + for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); + } +} + +void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) { + static const String export_method_string[] = { + "app-store", + "development", + "ad-hoc", + "enterprise" + }; + static const String storyboard_image_scale_mode[] = { + "center", + "scaleAspectFit", + "scaleAspectFill", + "scaleToFill" + }; + String str; + String strnew; + str.parse_utf8((const char *)pfile.ptr(), pfile.size()); + Vector<String> lines = str.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$binary") != -1) { + strnew += lines[i].replace("$binary", p_config.binary_name) + "\n"; + } else if (lines[i].find("$modules_buildfile") != -1) { + strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n"; + } else if (lines[i].find("$modules_fileref") != -1) { + strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n"; + } else if (lines[i].find("$modules_buildphase") != -1) { + strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n"; + } else if (lines[i].find("$modules_buildgrp") != -1) { + strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n"; + } else if (lines[i].find("$name") != -1) { + strnew += lines[i].replace("$name", p_config.pkg_name) + "\n"; + } else if (lines[i].find("$info") != -1) { + strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; + } else if (lines[i].find("$bundle_identifier") != -1) { + strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; + } else if (lines[i].find("$short_version") != -1) { + strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; + } else if (lines[i].find("$version") != -1) { + strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + } else if (lines[i].find("$signature") != -1) { + strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; + } else if (lines[i].find("$copyright") != -1) { + strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; + } else if (lines[i].find("$team_id") != -1) { + strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n"; + } else if (lines[i].find("$default_build_config") != -1) { + strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n"; + } else if (lines[i].find("$export_method") != -1) { + int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release"); + strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) { + strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { + strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid") != -1) { + String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release"); + strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n"; + } else if (lines[i].find("$code_sign_identity_debug") != -1) { + strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n"; + } else if (lines[i].find("$code_sign_identity_release") != -1) { + strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n"; + } else if (lines[i].find("$additional_plist_content") != -1) { + strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n"; + } else if (lines[i].find("$godot_archs") != -1) { + strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n"; + } else if (lines[i].find("$linker_flags") != -1) { + strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n"; + } else if (lines[i].find("$targeted_device_family") != -1) { + String xcode_value; + switch ((int)p_preset->get("application/targeted_device_family")) { + case 0: // iPhone + xcode_value = "1"; + break; + case 1: // iPad + xcode_value = "2"; + break; + case 2: // iPhone & iPad + xcode_value = "1,2"; + break; + } + strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n"; + } else if (lines[i].find("$cpp_code") != -1) { + strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n"; + } else if (lines[i].find("$docs_in_place") != -1) { + strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n"; + } else if (lines[i].find("$docs_sharing") != -1) { + strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n"; + } else if (lines[i].find("$entitlements_push_notifications") != -1) { + bool is_on = p_preset->get("capabilities/push_notifications"); + strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n"; + } else if (lines[i].find("$required_device_capabilities") != -1) { + String capabilities; + + // I've removed armv7 as we can run on 64bit only devices + // Note that capabilities listed here are requirements for the app to be installed. + // They don't enable anything. + Vector<String> capabilities_list = p_config.capabilities; + + if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) { + capabilities_list.push_back("wifi"); + } + + for (int idx = 0; idx < capabilities_list.size(); idx++) { + capabilities += "<string>" + capabilities_list[idx] + "</string>\n"; + } + + strnew += lines[i].replace("$required_device_capabilities", capabilities); + } else if (lines[i].find("$interface_orientations") != -1) { + String orientations; + const DisplayServer::ScreenOrientation screen_orientation = + DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))); + + switch (screen_orientation) { + case DisplayServer::SCREEN_LANDSCAPE: + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + break; + case DisplayServer::SCREEN_PORTRAIT: + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + break; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + break; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + // Allow both landscape orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + // Allow both portrait orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR: + // Allow all screen orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + } + + strnew += lines[i].replace("$interface_orientations", orientations); + } else if (lines[i].find("$camera_usage_description") != -1) { + String description = p_preset->get("privacy/camera_usage_description"); + strnew += lines[i].replace("$camera_usage_description", description) + "\n"; + } else if (lines[i].find("$microphone_usage_description") != -1) { + String description = p_preset->get("privacy/microphone_usage_description"); + strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; + } else if (lines[i].find("$photolibrary_usage_description") != -1) { + String description = p_preset->get("privacy/photolibrary_usage_description"); + strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n"; + } else if (lines[i].find("$plist_launch_screen_name") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>" : ""; + strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };" : ""; + strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */," : ""; + strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */," : ""; + strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : ""; + strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n"; + } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;"; + strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n"; + } else if (lines[i].find("$launch_screen_image_mode") != -1) { + int image_scale_mode = p_preset->get("storyboard/image_scale_mode"); + String value; + + switch (image_scale_mode) { + case 0: { + String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + // If custom logo is not specified, Godot does not scale default one, so we should do the same. + value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center"; + } break; + default: { + value = storyboard_image_scale_mode[image_scale_mode - 1]; + } + } + + strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; + } else if (lines[i].find("$launch_screen_background_color") != -1) { + bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); + Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; + + Dictionary value_dictionary; + value_dictionary["red"] = color.r; + value_dictionary["green"] = color.g; + value_dictionary["blue"] = color.b; + value_dictionary["alpha"] = color.a; + String value = value_format.format(value_dictionary, "$_"); + + strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; + } else { + strnew += lines[i] + "\n"; + } + } + + // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero... + // should apply the same fix in our OSX export. + CharString cs = strnew.utf8(); + pfile.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + pfile.write[i] = cs[i]; + } +} + +String EditorExportPlatformIOS::_get_additional_plist_content() { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + result += export_plugins[i]->get_ios_plist_content(); + } + return result; +} + +String EditorExportPlatformIOS::_get_linker_flags() { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + String flags = export_plugins[i]->get_ios_linker_flags(); + if (flags.length() == 0) { + continue; + } + if (result.length() > 0) { + result += ' '; + } + result += flags; + } + // the flags will be enclosed in quotes, so need to escape them + return result.replace("\"", "\\\""); +} + +String EditorExportPlatformIOS::_get_cpp_code() { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + result += export_plugins[i]->get_ios_cpp_code(); + } + return result; +} + +void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) { + ERR_FAIL_COND(p_dst.is_null()); + ERR_FAIL_COND(p_src.is_null()); + + int sw = p_rot ? p_src->get_height() : p_src->get_width(); + int sh = p_rot ? p_src->get_width() : p_src->get_height(); + + int x_pos = (p_dst->get_width() - sw) / 2; + int y_pos = (p_dst->get_height() - sh) / 2; + + int xs = (x_pos >= 0) ? 0 : -x_pos; + int ys = (y_pos >= 0) ? 0 : -y_pos; + + if (sw + x_pos > p_dst->get_width()) { + sw = p_dst->get_width() - x_pos; + } + if (sh + y_pos > p_dst->get_height()) { + sh = p_dst->get_height() - y_pos; + } + + for (int y = ys; y < sh; y++) { + for (int x = xs; x < sw; x++) { + Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y); + Color dc = p_dst->get_pixel(x_pos + x, y_pos + y); + dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); + dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); + dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); + dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); + p_dst->set_pixel(x_pos + x, y_pos + y, dc); + } + } +} + +struct IconInfo { + const char *preset_key; + const char *idiom; + const char *export_name; + const char *actual_size_side; + const char *scale; + const char *unscaled_size; + bool is_required = false; +}; + +static const IconInfo icon_infos[] = { + { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true }, + { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true }, + + { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true }, + { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, + + { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + + { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, + + { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + + { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, + + { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, + { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } +}; + +Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { + String json_description = "{\"images\":["; + String sizes; + + DirAccess *da = DirAccess::open(p_iconset_dir); + ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); + + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + IconInfo info = icon_infos[i]; + int side_size = String(info.actual_size_side).to_int(); + String icon_path = p_preset->get(info.preset_key); + if (icon_path.length() == 0) { + if ((bool)p_preset->get("icons/generate_missing")) { + // Resize main app icon + icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + return ERR_UNCONFIGURED; + } + img->resize(side_size, side_size); + err = img->save_png(p_iconset_dir + info.export_name); + if (err) { + String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } else { + if (info.is_required) { + String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset."; + ERR_PRINT(err_str); + return ERR_UNCONFIGURED; + } else { + String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset."; + WARN_PRINT(err_str); + } + continue; + } + } else { + // Load custom icon + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + return ERR_UNCONFIGURED; + } + if (img->get_width() != side_size || img->get_height() != side_size) { + ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'."); + return ERR_UNCONFIGURED; + } + + err = da->copy(icon_path, p_iconset_dir + info.export_name); + if (err) { + memdelete(da); + String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } + sizes += String(info.actual_size_side) + "\n"; + if (i > 0) { + json_description += ","; + } + json_description += String("{"); + json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; + json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; + json_description += String("\"scale\":") + "\"" + info.scale + "\","; + json_description += String("\"filename\":") + "\"" + info.export_name + "\""; + json_description += String("}"); + } + json_description += "]}"; + memdelete(da); + + FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); + ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE); + CharString json_utf8 = json_description.utf8(); + json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length()); + memdelete(json_file); + + FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE); + ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE); + CharString sizes_utf8 = sizes.utf8(); + sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length()); + memdelete(sizes_file); + + return OK; +} + +Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { + const String custom_launch_image_2x = p_preset->get("storyboard/custom_image@2x"); + const String custom_launch_image_3x = p_preset->get("storyboard/custom_image@3x"); + + if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) { + Ref<Image> image; + String image_path = p_dest_dir.plus_file("splash@2x.png"); + image.instantiate(); + Error err = image->load(custom_launch_image_2x); + + if (err) { + image.unref(); + return err; + } + + if (image->save_png(image_path) != OK) { + return ERR_FILE_CANT_WRITE; + } + + image.unref(); + image_path = p_dest_dir.plus_file("splash@3x.png"); + image.instantiate(); + err = image->load(custom_launch_image_3x); + + if (err) { + image.unref(); + return err; + } + + if (image->save_png(image_path) != OK) { + return ERR_FILE_CANT_WRITE; + } + } else { + Ref<Image> splash; + + const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + + if (!splash_path.is_empty()) { + splash.instantiate(); + const Error err = splash->load(splash_path); + if (err) { + splash.unref(); + } + } + + if (splash.is_null()) { + splash = Ref<Image>(memnew(Image(boot_splash_png))); + } + + // Using same image for both @2x and @3x + // because Godot's own boot logo uses single image for all resolutions. + // Also not using @1x image, because devices using this image variant + // are not supported by iOS 9, which is minimal target. + const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png"); + const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png"); + + if (splash->save_png(splash_png_path_2x) != OK) { + return ERR_FILE_CANT_WRITE; + } + + if (splash->save_png(splash_png_path_3x) != OK) { + return ERR_FILE_CANT_WRITE; + } + } + + return OK; +} + +Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { + DirAccess *da = DirAccess::open(p_dest_dir); + ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'."); + + for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + LoadingScreenInfo info = loading_screen_infos[i]; + String loading_screen_file = p_preset->get(info.preset_key); + if (loading_screen_file.size() > 0) { + // Load custom loading screens + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(loading_screen_file, img); + if (err != OK) { + ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); + return ERR_UNCONFIGURED; + } + if (img->get_width() != info.width || img->get_height() != info.height) { + ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); + return ERR_UNCONFIGURED; + } + err = da->copy(loading_screen_file, p_dest_dir + info.export_name); + if (err) { + memdelete(da); + String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'."; + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } else if ((bool)p_preset->get("launch_screens/generate_missing")) { + // Generate loading screen from the splash screen + Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + + Ref<Image> img = memnew(Image); + img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + img->fill(boot_bg_color); + + Ref<Image> img_bs; + + if (boot_logo_path.length() > 0) { + img_bs = Ref<Image>(memnew(Image)); + ImageLoader::load_image(boot_logo_path, img_bs); + } + if (!img_bs.is_valid()) { + img_bs = Ref<Image>(memnew(Image(boot_splash_png))); + } + if (img_bs.is_valid()) { + float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height(); + if (info.rotate) { + if (boot_logo_scale) { + if (info.width * aspect_ratio <= info.height) { + img_bs->resize(info.width * aspect_ratio, info.width); + } else { + img_bs->resize(info.height, info.height / aspect_ratio); + } + } + } else { + if (boot_logo_scale) { + if (info.height * aspect_ratio <= info.width) { + img_bs->resize(info.height * aspect_ratio, info.height); + } else { + img_bs->resize(info.width, info.width / aspect_ratio); + } + } + } + _blend_and_rotate(img, img_bs, info.rotate); + } + Error err = img->save_png(p_dest_dir + info.export_name); + if (err) { + String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen."; + WARN_PRINT(err_str.utf8().get_data()); + } + } else { + String err_str = String("No loading screen (") + info.preset_key + ") specified."; + WARN_PRINT(err_str.utf8().get_data()); + } + } + memdelete(da); + + return OK; +} + +Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) { + Vector<String> dirs; + String path; + String current_dir = p_da->get_current_dir(); + p_da->list_dir_begin(); + while ((path = p_da->get_next()).length() != 0) { + if (p_da->current_is_dir()) { + if (path != "." && path != "..") { + dirs.push_back(path); + } + } else { + Error err = p_handler(current_dir.plus_file(path), p_userdata); + if (err) { + p_da->list_dir_end(); + return err; + } + } + } + p_da->list_dir_end(); + + for (int i = 0; i < dirs.size(); ++i) { + String dir = dirs[i]; + p_da->change_dir(dir); + Error err = _walk_dir_recursive(p_da, p_handler, p_userdata); + p_da->change_dir(".."); + if (err) { + return err; + } + } + + return OK; +} + +struct CodesignData { + const Ref<EditorExportPreset> &preset; + bool debug = false; + + CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) : + preset(p_preset), + debug(p_debug) { + } +}; + +Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) { + if (p_file.ends_with(".dylib")) { + CodesignData *data = (CodesignData *)p_userdata; + print_line(String("Signing ") + p_file); + List<String> codesign_args; + codesign_args.push_back("-f"); + codesign_args.push_back("-s"); + codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release")); + codesign_args.push_back(p_file); + return OS::get_singleton()->execute("codesign", codesign_args); + } + return OK; +} + +struct PbxId { +private: + static char _hex_char(uint8_t four_bits) { + if (four_bits < 10) { + return ('0' + four_bits); + } + return 'A' + (four_bits - 10); + } + + static String _hex_pad(uint32_t num) { + Vector<char> ret; + ret.resize(sizeof(num) * 2); + for (uint64_t i = 0; i < sizeof(num) * 2; ++i) { + uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; + ret.write[i] = _hex_char(four_bits); + } + return String::utf8(ret.ptr(), ret.size()); + } + +public: + uint32_t high_bits; + uint32_t mid_bits; + uint32_t low_bits; + + String str() const { + return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits); + } + + PbxId &operator++() { + low_bits++; + if (!low_bits) { + mid_bits++; + if (!mid_bits) { + high_bits++; + } + } + + return *this; + } +}; + +struct ExportLibsData { + Vector<String> lib_paths; + String dest_dir; +}; + +void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { + // that is just a random number, we just need Godot IDs not to clash with + // existing IDs in the project. + PbxId current_id = { 0x58938401, 0, 0 }; + String pbx_files; + String pbx_frameworks_build; + String pbx_frameworks_refs; + String pbx_resources_build; + String pbx_resources_refs; + String pbx_embeded_frameworks; + + const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") + + "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"<group>\"; };\n"; + + for (int i = 0; i < p_additional_assets.size(); ++i) { + String additional_asset_info_format = file_info_format; + + String build_id = (++current_id).str(); + String ref_id = (++current_id).str(); + String framework_id = ""; + + const IOSExportAsset &asset = p_additional_assets[i]; + + String type; + if (asset.exported_path.ends_with(".framework")) { + if (asset.should_embed) { + additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; + framework_id = (++current_id).str(); + pbx_embeded_frameworks += framework_id + ",\n"; + } + + type = "wrapper.framework"; + } else if (asset.exported_path.ends_with(".xcframework")) { + if (asset.should_embed) { + additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; + framework_id = (++current_id).str(); + pbx_embeded_frameworks += framework_id + ",\n"; + } + + type = "wrapper.xcframework"; + } else if (asset.exported_path.ends_with(".dylib")) { + type = "compiled.mach-o.dylib"; + } else if (asset.exported_path.ends_with(".a")) { + type = "archive.ar"; + } else { + type = "file"; + } + + String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build; + String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs; + + if (pbx_build.length() > 0) { + pbx_build += ",\n"; + pbx_refs += ",\n"; + } + pbx_build += build_id; + pbx_refs += ref_id; + + Dictionary format_dict; + format_dict["build_id"] = build_id; + format_dict["ref_id"] = ref_id; + format_dict["name"] = asset.exported_path.get_file(); + format_dict["file_path"] = asset.exported_path; + format_dict["file_type"] = type; + if (framework_id.length() > 0) { + format_dict["framework_id"] = framework_id; + } + pbx_files += additional_asset_info_format.format(format_dict, "$_"); + } + + // Note, frameworks like gamekit are always included in our project.pbxprof file + // even if turned off in capabilities. + + String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size()); + str = str.replace("$additional_pbx_files", pbx_files); + str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build); + str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs); + str = str.replace("$additional_pbx_resources_build", pbx_resources_build); + str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs); + str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks); + + CharString cs = str.utf8(); + p_project_data.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + p_project_data.write[i] = cs[i]; + } +} + +Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { + DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'."); + + String binary_name = p_out_dir.get_file().get_basename(); + + DirAccess *da = DirAccess::create_for_path(p_asset); + if (!da) { + memdelete(filesystem_da); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + "."); + } + bool file_exists = da->file_exists(p_asset); + bool dir_exists = da->dir_exists(p_asset); + if (!file_exists && !dir_exists) { + memdelete(da); + memdelete(filesystem_da); + return ERR_FILE_NOT_FOUND; + } + + String base_dir = p_asset.get_base_dir().replace("res://", ""); + String destination_dir; + String destination; + String asset_path; + + bool create_framework = false; + + if (p_is_framework && p_asset.ends_with(".dylib")) { + // For iOS we need to turn .dylib into .framework + // to be able to send application to AppStore + asset_path = String("dylibs").plus_file(base_dir); + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_basename().get_file(); + } else { + file_name = *p_custom_file_name; + } + + String framework_name = file_name + ".framework"; + + asset_path = asset_path.plus_file(framework_name); + destination_dir = p_out_dir.plus_file(asset_path); + destination = destination_dir.plus_file(file_name); + create_framework = true; + } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) { + asset_path = String("dylibs").plus_file(base_dir); + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_file(); + } else { + file_name = *p_custom_file_name; + } + + asset_path = asset_path.plus_file(file_name); + destination_dir = p_out_dir.plus_file(asset_path); + destination = destination_dir; + } else { + asset_path = base_dir; + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_file(); + } else { + file_name = *p_custom_file_name; + } + + destination_dir = p_out_dir.plus_file(asset_path); + asset_path = asset_path.plus_file(file_name); + destination = p_out_dir.plus_file(asset_path); + } + + if (!filesystem_da->dir_exists(destination_dir)) { + Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); + if (make_dir_err) { + memdelete(da); + memdelete(filesystem_da); + return make_dir_err; + } + } + + Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination); + memdelete(da); + if (err) { + memdelete(filesystem_da); + return err; + } + IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; + r_exported_assets.push_back(exported_asset); + + if (create_framework) { + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_basename().get_file(); + } else { + file_name = *p_custom_file_name; + } + + String framework_name = file_name + ".framework"; + + // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib + { + List<String> install_name_args; + install_name_args.push_back("-id"); + install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name)); + install_name_args.push_back(destination); + + OS::get_singleton()->execute("install_name_tool", install_name_args); + } + + // Creating Info.plist + { + String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "<plist version=\"1.0\">\n" + "<dict>\n" + "<key>CFBundleShortVersionString</key>\n" + "<string>1.0</string>\n" + "<key>CFBundleIdentifier</key>\n" + "<string>com.gdnative.framework.$name</string>\n" + "<key>CFBundleName</key>\n" + "<string>$name</string>\n" + "<key>CFBundleExecutable</key>\n" + "<string>$name</string>\n" + "<key>DTPlatformName</key>\n" + "<string>iphoneos</string>\n" + "<key>CFBundleInfoDictionaryVersion</key>\n" + "<string>6.0</string>\n" + "<key>CFBundleVersion</key>\n" + "<string>1</string>\n" + "<key>CFBundlePackageType</key>\n" + "<string>FMWK</string>\n" + "<key>MinimumOSVersion</key>\n" + "<string>10.0</string>\n" + "</dict>\n" + "</plist>"; + + String info_plist = info_plist_format.replace("$name", file_name); + + FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); + if (f) { + f->store_string(info_plist); + f->close(); + memdelete(f); + } + } + } + + memdelete(filesystem_da); + + return OK; +} + +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { + for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { + String asset = p_assets[f_idx]; + if (!asset.begins_with("res://")) { + // either SDK-builtin or already a part of the export template + IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; + r_exported_assets.push_back(exported_asset); + } else { + Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + } + + return OK; +} + +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks(); + Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks(); + err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); + for (int j = 0; j < project_static_libs.size(); j++) { + project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project + } + err = _export_additional_assets(p_out_dir, project_static_libs, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files(); + err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + + Vector<String> library_paths; + for (int i = 0; i < p_libraries.size(); ++i) { + library_paths.push_back(p_libraries[i].path); + } + Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) { + Vector<ExportArchitecture> all_archs = _get_supported_architectures(); + Vector<String> enabled_archs; + for (int i = 0; i < all_archs.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + all_archs[i].name); + if (is_enabled) { + enabled_archs.push_back(all_archs[i].name); + } + } + return enabled_archs; +} + +Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) { + String plugin_definition_cpp_code; + String plugin_initialization_cpp_code; + String plugin_deinitialization_cpp_code; + + Vector<String> plugin_linked_dependencies; + Vector<String> plugin_embedded_dependencies; + Vector<String> plugin_files; + + Vector<PluginConfigIOS> enabled_plugins = get_enabled_plugins(p_preset); + + Vector<String> added_linked_dependenciy_names; + Vector<String> added_embedded_dependenciy_names; + HashMap<String, String> plist_values; + + Set<String> plugin_linker_flags; + + Error err; + + for (int i = 0; i < enabled_plugins.size(); i++) { + PluginConfigIOS plugin = enabled_plugins[i]; + + // Export plugin binary. + String plugin_main_binary = PluginConfigIOS::get_plugin_main_binary(plugin, p_debug); + String plugin_binary_result_file = plugin.binary.get_file(); + // We shouldn't embed .xcframework that contains static libraries. + // Static libraries are not embedded anyway. + err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); + + ERR_FAIL_COND_V(err, err); + + // Adding dependencies. + // Use separate container for names to check for duplicates. + for (int j = 0; j < plugin.linked_dependencies.size(); j++) { + String dependency = plugin.linked_dependencies[j]; + String name = dependency.get_file(); + + if (added_linked_dependenciy_names.has(name)) { + continue; + } + + added_linked_dependenciy_names.push_back(name); + plugin_linked_dependencies.push_back(dependency); + } + + for (int j = 0; j < plugin.system_dependencies.size(); j++) { + String dependency = plugin.system_dependencies[j]; + String name = dependency.get_file(); + + if (added_linked_dependenciy_names.has(name)) { + continue; + } + + added_linked_dependenciy_names.push_back(name); + plugin_linked_dependencies.push_back(dependency); + } + + for (int j = 0; j < plugin.embedded_dependencies.size(); j++) { + String dependency = plugin.embedded_dependencies[j]; + String name = dependency.get_file(); + + if (added_embedded_dependenciy_names.has(name)) { + continue; + } + + added_embedded_dependenciy_names.push_back(name); + plugin_embedded_dependencies.push_back(dependency); + } + + plugin_files.append_array(plugin.files_to_copy); + + // Capabilities + // Also checking for duplicates. + for (int j = 0; j < plugin.capabilities.size(); j++) { + String capability = plugin.capabilities[j]; + + if (p_config_data.capabilities.has(capability)) { + continue; + } + + p_config_data.capabilities.push_back(capability); + } + + // Linker flags + // Checking duplicates + for (int j = 0; j < plugin.linker_flags.size(); j++) { + String linker_flag = plugin.linker_flags[j]; + plugin_linker_flags.insert(linker_flag); + } + + // Plist + // Using hash map container to remove duplicates + const String *K = nullptr; + + while ((K = plugin.plist.next(K))) { + String key = *K; + PluginConfigIOS::PlistItem item = plugin.plist[key]; + + String value; + + switch (item.type) { + case PluginConfigIOS::PlistItemType::STRING_INPUT: { + String preset_name = "plugins_plist/" + key; + String input_value = p_preset->get(preset_name); + value = "<string>" + input_value + "</string>"; + } break; + default: + value = item.value; + break; + } + + if (key.is_empty() || value.is_empty()) { + continue; + } + + String plist_key = "<key>" + key + "</key>"; + + plist_values[plist_key] = value; + } + + // CPP Code + String definition_comment = "// Plugin: " + plugin.name + "\n"; + String initialization_method = plugin.initialization_method + "();\n"; + String deinitialization_method = plugin.deinitialization_method + "();\n"; + + plugin_definition_cpp_code += definition_comment + + "extern void " + initialization_method + + "extern void " + deinitialization_method + "\n"; + + plugin_initialization_cpp_code += "\t" + initialization_method; + plugin_deinitialization_cpp_code += "\t" + deinitialization_method; + } + + // Updating `Info.plist` + { + const String *K = nullptr; + while ((K = plist_values.next(K))) { + String key = *K; + String value = plist_values[key]; + + if (key.is_empty() || value.is_empty()) { + continue; + } + + p_config_data.plist_content += key + value + "\n"; + } + } + + // Export files + { + // Export linked plugin dependency + err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + // Export embedded plugin dependency + err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + // Export plugin files + err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + + // Update CPP + { + Dictionary plugin_format; + plugin_format["definition"] = plugin_definition_cpp_code; + plugin_format["initialization"] = plugin_initialization_cpp_code; + plugin_format["deinitialization"] = plugin_deinitialization_cpp_code; + + String plugin_cpp_code = "\n// Godot Plugins\n" + "void godot_ios_plugins_initialize();\n" + "void godot_ios_plugins_deinitialize();\n" + "// Exported Plugins\n\n" + "$definition" + "// Use Plugins\n" + "void godot_ios_plugins_initialize() {\n" + "$initialization" + "}\n\n" + "void godot_ios_plugins_deinitialize() {\n" + "$deinitialization" + "}\n"; + + p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_"); + } + + // Update Linker Flag Values + { + String result_linker_flags = " "; + for (Set<String>::Element *E = plugin_linker_flags.front(); E; E = E->next()) { + const String &flag = E->get(); + + if (flag.length() == 0) { + continue; + } + + if (result_linker_flags.length() > 0) { + result_linker_flags += ' '; + } + + result_linker_flags += flag; + } + result_linker_flags = result_linker_flags.replace("\"", "\\\""); + p_config_data.linker_flags += result_linker_flags; + } + + return OK; +} + +Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_pkg_name; + String dest_dir = p_path.get_base_dir() + "/"; + String binary_name = p_path.get_file().get_basename(); + + EditorProgress ep("export", "Exporting for iOS", 5, true); + + String team_id = p_preset->get("application/app_store_team_id"); + ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project."); + + if (p_debug) { + src_pkg_name = p_preset->get("custom_template/debug"); + } else { + src_pkg_name = p_preset->get("custom_template/release"); + } + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("iphone.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(dest_dir)) { + return ERR_FILE_BAD_PATH; + } + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (da) { + String current_dir = da->get_current_dir(); + + // remove leftovers from last export so they don't interfere + // in case some files are no longer needed + if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) { + da->erase_contents_recursive(); + } + if (da->change_dir(dest_dir + binary_name) == OK) { + da->erase_contents_recursive(); + } + + da->change_dir(current_dir); + + if (!da->dir_exists(dest_dir + binary_name)) { + Error err = da->make_dir(dest_dir + binary_name); + if (err) { + memdelete(da); + return err; + } + } + memdelete(da); + } + + if (ep.step("Making .pck", 0)) { + return ERR_SKIP; + } + String pack_path = dest_dir + binary_name + ".pck"; + Vector<SharedObject> libraries; + Error err = save_pack(p_preset, pack_path, &libraries); + if (err) { + return err; + } + + if (ep.step("Extracting and configuring Xcode project", 1)) { + return ERR_SKIP; + } + + String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".xcframework"; + + print_line("Static framework: " + library_to_use); + String pkg_name; + if (p_preset->get("application/name") != "") { + pkg_name = p_preset->get("application/name"); // app_name + } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + } else { + pkg_name = "Unnamed"; + } + + bool found_library = false; + int total_size = 0; + + const String project_file = "godot_ios.xcodeproj/project.pbxproj"; + Set<String> files_to_parse; + files_to_parse.insert("godot_ios/godot_ios-Info.plist"); + files_to_parse.insert(project_file); + files_to_parse.insert("godot_ios/export_options.plist"); + files_to_parse.insert("godot_ios/dummy.cpp"); + files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata"); + files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); + files_to_parse.insert("godot_ios/godot_ios.entitlements"); + files_to_parse.insert("godot_ios/Launch Screen.storyboard"); + + IOSConfigData config_data = { + pkg_name, + binary_name, + _get_additional_plist_content(), + String(" ").join(_get_preset_architectures(p_preset)), + _get_linker_flags(), + _get_cpp_code(), + "", + "", + "", + "", + Vector<String>() + }; + + Vector<IOSExportAsset> assets; + + DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); + + print_line("Unzipping..."); + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name); + return ERR_CANT_OPEN; + } + + err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug); + ERR_FAIL_COND_V(err, err); + + //export rest of the files + int ret = unzGoToFirstFile(src_pkg_zip); + Vector<uint8_t> project_file_data; + while (ret == UNZ_OK) { +#if defined(OSX_ENABLED) || defined(X11_ENABLED) + bool is_execute = false; +#endif + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String file = fname; + + print_line("READ: " + file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + //write + + file = file.replace_first("iphone/", ""); + + if (files_to_parse.has(file)) { + _fix_config_file(p_preset, data, config_data, p_debug); + } else if (file.begins_with("libgodot.iphone")) { + if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) { + ret = unzGoToNextFile(src_pkg_zip); + continue; //ignore! + } + found_library = true; +#if defined(OSX_ENABLED) || defined(X11_ENABLED) + is_execute = true; +#endif + file = file.replace(library_to_use, binary_name + ".xcframework"); + } + + if (file == project_file) { + project_file_data = data; + } + + ///@TODO need to parse logo files + + if (data.size() > 0) { + file = file.replace("godot_ios", binary_name); + + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + /* write it into our folder structure */ + file = dest_dir + file; + + /* make sure this folder exists */ + String dir_name = file.get_base_dir(); + if (!tmp_app_path->dir_exists(dir_name)) { + print_line("Creating " + dir_name); + Error dir_err = tmp_app_path->make_dir_recursive(dir_name); + if (dir_err) { + ERR_PRINT("Can't create '" + dir_name + "'."); + unzClose(src_pkg_zip); + memdelete(tmp_app_path); + return ERR_CANT_CREATE; + } + } + + /* write the file */ + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (!f) { + ERR_PRINT("Can't write '" + file + "'."); + unzClose(src_pkg_zip); + memdelete(tmp_app_path); + return ERR_CANT_CREATE; + }; + f->store_buffer(data.ptr(), data.size()); + f->close(); + memdelete(f); + +#if defined(OSX_ENABLED) || defined(X11_ENABLED) + if (is_execute) { + // we need execute rights on this file + chmod(file.utf8().get_data(), 0755); + } +#endif + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + /* we're done with our source zip */ + unzClose(src_pkg_zip); + + if (!found_library) { + ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive."); + memdelete(tmp_app_path); + return ERR_FILE_NOT_FOUND; + } + + // Copy project static libs to the project + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); + for (int j = 0; j < project_static_libs.size(); j++) { + const String &static_lib_path = project_static_libs[j]; + String dest_lib_file_path = dest_dir + static_lib_path.get_file(); + Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path); + if (lib_copy_err != OK) { + ERR_PRINT("Can't copy '" + static_lib_path + "'."); + memdelete(tmp_app_path); + return lib_copy_err; + } + } + } + + String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; + err = OK; + if (!tmp_app_path->dir_exists(iconset_dir)) { + err = tmp_app_path->make_dir_recursive(iconset_dir); + } + memdelete(tmp_app_path); + if (err) { + return err; + } + + err = _export_icons(p_preset, iconset_dir); + if (err) { + return err; + } + + bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard"); + + String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/"; + String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/"; + + DirAccess *launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + if (!launch_screen_da) { + return ERR_CANT_CREATE; + } + + if (use_storyboard) { + print_line("Using Launch Storyboard"); + + if (launch_screen_da->change_dir(launch_image_path) == OK) { + launch_screen_da->erase_contents_recursive(); + launch_screen_da->remove(launch_image_path); + } + + err = _export_loading_screen_file(p_preset, splash_image_path); + } else { + print_line("Using Launch Images"); + + const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard"; + + launch_screen_da->remove(launch_screen_path); + + if (launch_screen_da->change_dir(splash_image_path) == OK) { + launch_screen_da->erase_contents_recursive(); + launch_screen_da->remove(splash_image_path); + } + + err = _export_loading_screen_images(p_preset, launch_image_path); + } + + memdelete(launch_screen_da); + + if (err) { + return err; + } + + print_line("Exporting additional assets"); + _export_additional_assets(dest_dir + binary_name, libraries, assets); + _add_assets_to_project(p_preset, project_file_data, assets); + String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; + FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE); + if (!f) { + ERR_PRINT("Can't write '" + project_file_name + "'."); + return ERR_CANT_CREATE; + }; + f->store_buffer(project_file_data.ptr(), project_file_data.size()); + f->close(); + memdelete(f); + +#ifdef OSX_ENABLED + if (ep.step("Code-signing dylibs", 2)) { + return ERR_SKIP; + } + DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); + ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); + CodesignData codesign_data(p_preset, p_debug); + err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); + memdelete(dylibs_dir); + ERR_FAIL_COND_V(err, err); + + if (ep.step("Making .xcarchive", 3)) { + return ERR_SKIP; + } + String archive_path = p_path.get_basename() + ".xcarchive"; + List<String> archive_args; + archive_args.push_back("-project"); + archive_args.push_back(dest_dir + binary_name + ".xcodeproj"); + archive_args.push_back("-scheme"); + archive_args.push_back(binary_name); + archive_args.push_back("-sdk"); + archive_args.push_back("iphoneos"); + archive_args.push_back("-configuration"); + archive_args.push_back(p_debug ? "Debug" : "Release"); + archive_args.push_back("-destination"); + archive_args.push_back("generic/platform=iOS"); + archive_args.push_back("archive"); + archive_args.push_back("-archivePath"); + archive_args.push_back(archive_path); + err = OS::get_singleton()->execute("xcodebuild", archive_args); + ERR_FAIL_COND_V(err, err); + + if (ep.step("Making .ipa", 4)) { + return ERR_SKIP; + } + List<String> export_args; + export_args.push_back("-exportArchive"); + export_args.push_back("-archivePath"); + export_args.push_back(archive_path); + export_args.push_back("-exportOptionsPlist"); + export_args.push_back(dest_dir + binary_name + "/export_options.plist"); + export_args.push_back("-allowProvisioningUpdates"); + export_args.push_back("-exportPath"); + export_args.push_back(dest_dir); + err = OS::get_singleton()->execute("xcodebuild", export_args); + ERR_FAIL_COND_V(err, err); +#else + print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package."); +#endif + + return OK; +} + +bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + bool dvalid = exists_export_template("iphone.zip", &err); + bool rvalid = dvalid; // Both in the same ZIP. + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + // Validate the rest of the configuration. + + String team_id = p_preset->get("application/app_store_team_id"); + if (team_id.length() == 0) { + err += TTR("App Store Team ID not specified - cannot configure the project.") + "\n"; + valid = false; + } + + String identifier = p_preset->get("application/bundle_identifier"); + String pn_err; + if (!is_package_name_valid(identifier, &pn_err)) { + err += TTR("Invalid Identifier:") + " " + pn_err + "\n"; + valid = false; + } + + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + IconInfo info = icon_infos[i]; + String icon_path = p_preset->get(info.preset_key); + if (icon_path.length() == 0) { + if (info.is_required) { + err += TTR("Required icon is not specified in the preset.") + "\n"; + valid = false; + } + break; + } + } + + String etc_error = test_etc2_or_pvrtc(); + if (etc_error != String()) { + valid = false; + err += etc_error; + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +EditorExportPlatformIOS::EditorExportPlatformIOS() { + Ref<Image> img = memnew(Image(_iphone_logo)); + logo.instantiate(); + logo->create_from_image(img); + + plugins_changed.set(); + + check_for_changes_thread.start(_check_for_changes_poll_thread, this); +} + +EditorExportPlatformIOS::~EditorExportPlatformIOS() { + quit_request.set(); + check_for_changes_thread.wait_to_finish(); +} diff --git a/platform/iphone/export/export_plugin.h b/platform/iphone/export/export_plugin.h new file mode 100644 index 0000000000..8d3af6e057 --- /dev/null +++ b/platform/iphone/export/export_plugin.h @@ -0,0 +1,296 @@ +/*************************************************************************/ +/* export_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 IPHONE_EXPORT_PLUGIN_H +#define IPHONE_EXPORT_PLUGIN_H + +#include "core/config/project_settings.h" +#include "core/io/file_access.h" +#include "core/io/image_loader.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/os.h" +#include "core/templates/safe_refcount.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "main/splash.gen.h" +#include "platform/iphone/logo.gen.h" +#include "string.h" + +#include "godot_plugin_config.h" + +#include <sys/stat.h> + +class EditorExportPlatformIOS : public EditorExportPlatform { + GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); + + int version_code; + + Ref<ImageTexture> logo; + + // Plugins + SafeFlag plugins_changed; + Thread check_for_changes_thread; + SafeFlag quit_request; + Mutex plugins_lock; + Vector<PluginConfigIOS> plugins; + + typedef Error (*FileHandler)(String p_file, void *p_userdata); + static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata); + static Error _codesign(String p_file, void *p_userdata); + void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot); + + struct IOSConfigData { + String pkg_name; + String binary_name; + String plist_content; + String architectures; + String linker_flags; + String cpp_code; + String modules_buildfile; + String modules_fileref; + String modules_buildphase; + String modules_buildgrp; + Vector<String> capabilities; + }; + struct ExportArchitecture { + String name; + bool is_default = false; + + ExportArchitecture() {} + + ExportArchitecture(String p_name, bool p_is_default) { + name = p_name; + is_default = p_is_default; + } + }; + + struct IOSExportAsset { + String exported_path; + bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource + bool should_embed = false; + }; + + String _get_additional_plist_content(); + String _get_linker_flags(); + String _get_cpp_code(); + void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug); + Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); + Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); + Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); + + Vector<ExportArchitecture> _get_supported_architectures(); + Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); + + void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); + Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); + Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); + Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); + Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); + + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = TTR("Identifier is missing."); + } + return false; + } + + for (int i = 0; i < pname.length(); i++) { + char32_t c = pname[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { + if (r_error) { + *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); + } + return false; + } + } + + return true; + } + + static void _check_for_changes_poll_thread(void *ud) { + EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud; + + while (!ea->quit_request.is_set()) { + // Nothing to do if we already know the plugins have changed. + if (!ea->plugins_changed.is_set()) { + MutexLock lock(ea->plugins_lock); + + Vector<PluginConfigIOS> loaded_plugins = get_plugins(); + + if (ea->plugins.size() != loaded_plugins.size()) { + ea->plugins_changed.set(); + } else { + for (int i = 0; i < ea->plugins.size(); i++) { + if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) { + ea->plugins_changed.set(); + break; + } + } + } + } + + uint64_t wait = 3000000; + uint64_t time = OS::get_singleton()->get_ticks_usec(); + while (OS::get_singleton()->get_ticks_usec() - time < wait) { + OS::get_singleton()->delay_usec(300000); + + if (ea->quit_request.is_set()) { + break; + } + } + } + } + +protected: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + virtual void get_export_options(List<ExportOption> *r_options) override; + +public: + virtual String get_name() const override { return "iOS"; } + virtual String get_os_name() const override { return "iOS"; } + virtual Ref<Texture2D> get_logo() const override { return logo; } + + virtual bool should_update_export_options() override { + bool export_options_changed = plugins_changed.is_set(); + if (export_options_changed) { + // don't clear unless we're reporting true, to avoid race + plugins_changed.clear(); + } + return export_options_changed; + } + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { + List<String> list; + list.push_back("ipa"); + return list; + } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual void get_platform_features(List<String> *r_features) override { + r_features->push_back("mobile"); + r_features->push_back("iOS"); + } + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { + } + + EditorExportPlatformIOS(); + ~EditorExportPlatformIOS(); + + /// List the gdip files in the directory specified by the p_path parameter. + static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) { + Vector<String> dir_files; + DirAccessRef da = DirAccess::open(p_path); + if (da) { + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file.is_empty()) { + break; + } + + if (file == "." || file == "..") { + continue; + } + + if (da->current_is_hidden()) { + continue; + } + + if (da->current_is_dir()) { + if (p_check_directories) { + Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false); + for (int i = 0; i < directory_files.size(); ++i) { + dir_files.push_back(file.plus_file(directory_files[i])); + } + } + + continue; + } + + if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) { + dir_files.push_back(file); + } + } + da->list_dir_end(); + } + + return dir_files; + } + + static Vector<PluginConfigIOS> get_plugins() { + Vector<PluginConfigIOS> loaded_plugins; + + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins"); + + if (DirAccess::exists(plugins_dir)) { + Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true); + + if (!plugins_filenames.is_empty()) { + Ref<ConfigFile> config_file = memnew(ConfigFile); + for (int i = 0; i < plugins_filenames.size(); i++) { + PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + if (config.valid_config) { + loaded_plugins.push_back(config); + } else { + print_error("Invalid plugin config file " + plugins_filenames[i]); + } + } + } + } + + return loaded_plugins; + } + + static Vector<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { + Vector<PluginConfigIOS> enabled_plugins; + Vector<PluginConfigIOS> all_plugins = get_plugins(); + for (int i = 0; i < all_plugins.size(); i++) { + PluginConfigIOS plugin = all_plugins[i]; + bool enabled = p_presets->get("plugins/" + plugin.name); + if (enabled) { + enabled_plugins.push_back(plugin); + } + } + + return enabled_plugins; + } +}; + +#endif diff --git a/platform/iphone/plugin/godot_plugin_config.h b/platform/iphone/export/godot_plugin_config.cpp index f9c5d7e51f..9d0324f41a 100644 --- a/platform/iphone/plugin/godot_plugin_config.h +++ b/platform/iphone/export/godot_plugin_config.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* godot_plugin_config.h */ +/* godot_plugin_config.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,92 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GODOT_PLUGIN_CONFIG_H -#define GODOT_PLUGIN_CONFIG_H - -#include "core/error/error_list.h" -#include "core/io/config_file.h" -#include "core/string/ustring.h" - -/* - The `config` section and fields are required and defined as follow: -- **name**: name of the plugin -- **binary**: path to static `.a` library - -The `dependencies` and fields are optional. -- **linked**: dependencies that should only be linked. -- **embedded**: dependencies that should be linked and embedded into application. -- **system**: system dependencies that should be linked. -- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file. -- **files**: files that would be copied into application - -The `plist` section are optional. -- **key**: key and value that would be added in Info.plist file. - */ - -struct PluginConfigIOS { - inline static const char *PLUGIN_CONFIG_EXT = ".gdip"; - - inline static const char *CONFIG_SECTION = "config"; - inline static const char *CONFIG_NAME_KEY = "name"; - inline static const char *CONFIG_BINARY_KEY = "binary"; - inline static const char *CONFIG_INITIALIZE_KEY = "initialization"; - inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization"; - - inline static const char *DEPENDENCIES_SECTION = "dependencies"; - inline static const char *DEPENDENCIES_LINKED_KEY = "linked"; - inline static const char *DEPENDENCIES_EMBEDDED_KEY = "embedded"; - inline static const char *DEPENDENCIES_SYSTEM_KEY = "system"; - inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities"; - inline static const char *DEPENDENCIES_FILES_KEY = "files"; - inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags"; - - inline static const char *PLIST_SECTION = "plist"; - - enum PlistItemType { - UNKNOWN, - STRING, - INTEGER, - BOOLEAN, - RAW, - STRING_INPUT, - }; - - struct PlistItem { - PlistItemType type; - String value; - }; - - // Set to true when the config file is properly loaded. - bool valid_config = false; - bool supports_targets = false; - // Unix timestamp of last change to this plugin. - uint64_t last_updated = 0; - - // Required config section - String name; - String binary; - String initialization_method; - String deinitialization_method; - - // Optional dependencies section - Vector<String> linked_dependencies; - Vector<String> embedded_dependencies; - Vector<String> system_dependencies; - - Vector<String> files_to_copy; - Vector<String> capabilities; - - Vector<String> linker_flags; - - // Optional plist section - // String value is default value. - // Currently supports `string`, `boolean`, `integer`, `raw`, `string_input` types - // <name>:<type> = <value> - HashMap<String, PlistItem> plist; -}; - -static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { +#include "godot_plugin_config.h" + +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" + +String PluginConfigIOS::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { String absolute_path; if (dependency_path.is_empty()) { @@ -130,7 +51,7 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str return absolute_path.replace(res_path, "res://"); } -static inline String resolve_system_dependency_path(String dependency_path) { +String PluginConfigIOS::resolve_system_dependency_path(String dependency_path) { String absolute_path; if (dependency_path.is_empty()) { @@ -146,7 +67,7 @@ static inline String resolve_system_dependency_path(String dependency_path) { return system_path.plus_file(dependency_path); } -static inline Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) { +Vector<String> PluginConfigIOS::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) { Vector<String> paths; for (int i = 0; i < p_paths.size(); i++) { @@ -162,7 +83,7 @@ static inline Vector<String> resolve_local_dependencies(String plugin_config_dir return paths; } -static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) { +Vector<String> PluginConfigIOS::resolve_system_dependencies(Vector<String> p_paths) { Vector<String> paths; for (int i = 0; i < p_paths.size(); i++) { @@ -178,7 +99,7 @@ static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) return paths; } -static inline bool validate_plugin(PluginConfigIOS &plugin_config) { +bool PluginConfigIOS::validate_plugin(PluginConfigIOS &plugin_config) { bool valid_name = !plugin_config.name.is_empty(); bool valid_binary_name = !plugin_config.binary.is_empty(); bool valid_initialize = !plugin_config.initialization_method.is_empty(); @@ -213,7 +134,7 @@ static inline bool validate_plugin(PluginConfigIOS &plugin_config) { return plugin_config.valid_config; } -static inline String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug) { +String PluginConfigIOS::get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug) { if (!plugin_config.supports_targets) { return plugin_config.binary; } @@ -226,7 +147,7 @@ static inline String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool return plugin_binary_dir.plus_file(plugin_file); } -static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) { +uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) { uint64_t last_updated = FileAccess::get_modified_time(config_path); if (!plugin_config.supports_targets) { @@ -245,7 +166,7 @@ static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugi return last_updated; } -static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path) { +PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file, const String &path) { PluginConfigIOS plugin_config = {}; if (!config_file.is_valid()) { @@ -362,5 +283,3 @@ static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, co return plugin_config; } - -#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/iphone/export/godot_plugin_config.h b/platform/iphone/export/godot_plugin_config.h new file mode 100644 index 0000000000..1add281627 --- /dev/null +++ b/platform/iphone/export/godot_plugin_config.h @@ -0,0 +1,132 @@ +/*************************************************************************/ +/* godot_plugin_config.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 IPHONE_GODOT_PLUGIN_CONFIG_H +#define IPHONE_GODOT_PLUGIN_CONFIG_H + +#include "core/error/error_list.h" +#include "core/io/config_file.h" +#include "core/string/ustring.h" + +/* + The `config` section and fields are required and defined as follow: +- **name**: name of the plugin +- **binary**: path to static `.a` library + +The `dependencies` and fields are optional. +- **linked**: dependencies that should only be linked. +- **embedded**: dependencies that should be linked and embedded into application. +- **system**: system dependencies that should be linked. +- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file. +- **files**: files that would be copied into application + +The `plist` section are optional. +- **key**: key and value that would be added in Info.plist file. + */ + +struct PluginConfigIOS { + inline static const char *PLUGIN_CONFIG_EXT = ".gdip"; + + inline static const char *CONFIG_SECTION = "config"; + inline static const char *CONFIG_NAME_KEY = "name"; + inline static const char *CONFIG_BINARY_KEY = "binary"; + inline static const char *CONFIG_INITIALIZE_KEY = "initialization"; + inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization"; + + inline static const char *DEPENDENCIES_SECTION = "dependencies"; + inline static const char *DEPENDENCIES_LINKED_KEY = "linked"; + inline static const char *DEPENDENCIES_EMBEDDED_KEY = "embedded"; + inline static const char *DEPENDENCIES_SYSTEM_KEY = "system"; + inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities"; + inline static const char *DEPENDENCIES_FILES_KEY = "files"; + inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags"; + + inline static const char *PLIST_SECTION = "plist"; + + enum PlistItemType { + UNKNOWN, + STRING, + INTEGER, + BOOLEAN, + RAW, + STRING_INPUT, + }; + + struct PlistItem { + PlistItemType type; + String value; + }; + + // Set to true when the config file is properly loaded. + bool valid_config = false; + bool supports_targets = false; + // Unix timestamp of last change to this plugin. + uint64_t last_updated = 0; + + // Required config section + String name; + String binary; + String initialization_method; + String deinitialization_method; + + // Optional dependencies section + Vector<String> linked_dependencies; + Vector<String> embedded_dependencies; + Vector<String> system_dependencies; + + Vector<String> files_to_copy; + Vector<String> capabilities; + + Vector<String> linker_flags; + + // Optional plist section + // String value is default value. + // Currently supports `string`, `boolean`, `integer`, `raw`, `string_input` types + // <name>:<type> = <value> + HashMap<String, PlistItem> plist; + + static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path); + + static String resolve_system_dependency_path(String dependency_path); + + static Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths); + + static Vector<String> resolve_system_dependencies(Vector<String> p_paths); + + static bool validate_plugin(PluginConfigIOS &plugin_config); + + static String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug); + + static uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path); + + static PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path); +}; + +#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index bf4244eda4..889b0bbd02 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -28,971 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/image_loader.h" -#include "core/io/stream_peer_ssl.h" -#include "core/io/tcp_server.h" -#include "core/io/zip_io.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "main/splash.gen.h" -#include "platform/javascript/logo.gen.h" -#include "platform/javascript/run_icon.gen.h" +#include "export.h" -class EditorHTTPServer : public RefCounted { -private: - Ref<TCPServer> server; - Map<String, String> mimes; - Ref<StreamPeerTCP> tcp; - Ref<StreamPeerSSL> ssl; - Ref<StreamPeer> peer; - Ref<CryptoKey> key; - Ref<X509Certificate> cert; - bool use_ssl = false; - uint64_t time = 0; - uint8_t req_buf[4096]; - int req_pos = 0; - - void _clear_client() { - peer = Ref<StreamPeer>(); - ssl = Ref<StreamPeerSSL>(); - tcp = Ref<StreamPeerTCP>(); - memset(req_buf, 0, sizeof(req_buf)); - time = 0; - req_pos = 0; - } - - void _set_internal_certs(Ref<Crypto> p_crypto) { - const String cache_path = EditorPaths::get_singleton()->get_cache_dir(); - const String key_path = cache_path.plus_file("html5_server.key"); - const String crt_path = cache_path.plus_file("html5_server.crt"); - bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path); - if (!regen) { - key = Ref<CryptoKey>(CryptoKey::create()); - cert = Ref<X509Certificate>(X509Certificate::create()); - if (key->load(key_path) != OK || cert->load(crt_path) != OK) { - regen = true; - } - } - if (regen) { - key = p_crypto->generate_rsa(2048); - key->save(key_path); - cert = p_crypto->generate_self_signed_certificate(key, "CN=godot-debug.local,O=A Game Dev,C=XXA", "20140101000000", "20340101000000"); - cert->save(crt_path); - } - } - -public: - EditorHTTPServer() { - mimes["html"] = "text/html"; - mimes["js"] = "application/javascript"; - mimes["json"] = "application/json"; - mimes["pck"] = "application/octet-stream"; - mimes["png"] = "image/png"; - mimes["svg"] = "image/svg"; - mimes["wasm"] = "application/wasm"; - server.instantiate(); - stop(); - } - - void stop() { - server->stop(); - _clear_client(); - } - - Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) { - use_ssl = p_use_ssl; - if (use_ssl) { - Ref<Crypto> crypto = Crypto::create(); - if (crypto.is_null()) { - return ERR_UNAVAILABLE; - } - if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) { - key = Ref<CryptoKey>(CryptoKey::create()); - Error err = key->load(p_ssl_key); - ERR_FAIL_COND_V(err != OK, err); - cert = Ref<X509Certificate>(X509Certificate::create()); - err = cert->load(p_ssl_cert); - ERR_FAIL_COND_V(err != OK, err); - } else { - _set_internal_certs(crypto); - } - } - return server->listen(p_port, p_address); - } - - bool is_listening() const { - return server->is_listening(); - } - - void _send_response() { - Vector<String> psa = String((char *)req_buf).split("\r\n"); - int len = psa.size(); - ERR_FAIL_COND_MSG(len < 4, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); - - Vector<String> req = psa[0].split(" ", false); - ERR_FAIL_COND_MSG(req.size() < 2, "Invalid protocol or status code."); - - // Wrong protocol - ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version."); - - const int query_index = req[1].find_char('?'); - const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index); - - const String req_file = path.get_file(); - const String req_ext = path.get_extension(); - const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); - const String filepath = cache_path.plus_file(req_file); - - if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) { - String s = "HTTP/1.1 404 Not Found\r\n"; - s += "Connection: Close\r\n"; - s += "\r\n"; - CharString cs = s.utf8(); - peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); - return; - } - const String ctype = mimes[req_ext]; - - FileAccess *f = FileAccess::open(filepath, FileAccess::READ); - ERR_FAIL_COND(!f); - String s = "HTTP/1.1 200 OK\r\n"; - s += "Connection: Close\r\n"; - s += "Content-Type: " + ctype + "\r\n"; - s += "Access-Control-Allow-Origin: *\r\n"; - s += "Cross-Origin-Opener-Policy: same-origin\r\n"; - s += "Cross-Origin-Embedder-Policy: require-corp\r\n"; - s += "Cache-Control: no-store, max-age=0\r\n"; - s += "\r\n"; - CharString cs = s.utf8(); - Error err = peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); - if (err != OK) { - memdelete(f); - ERR_FAIL(); - } - - while (true) { - uint8_t bytes[4096]; - uint64_t read = f->get_buffer(bytes, 4096); - if (read == 0) { - break; - } - err = peer->put_data(bytes, read); - if (err != OK) { - memdelete(f); - ERR_FAIL(); - } - } - memdelete(f); - } - - void poll() { - if (!server->is_listening()) { - return; - } - if (tcp.is_null()) { - if (!server->is_connection_available()) { - return; - } - tcp = server->take_connection(); - peer = tcp; - time = OS::get_singleton()->get_ticks_usec(); - } - if (OS::get_singleton()->get_ticks_usec() - time > 1000000) { - _clear_client(); - return; - } - if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - return; - } - - if (use_ssl) { - if (ssl.is_null()) { - ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); - peer = ssl; - ssl->set_blocking_handshake_enabled(false); - if (ssl->accept_stream(tcp, key, cert) != OK) { - _clear_client(); - return; - } - } - ssl->poll(); - if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { - // Still handshaking, keep waiting. - return; - } - if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { - _clear_client(); - return; - } - } - - while (true) { - char *r = (char *)req_buf; - int l = req_pos - 1; - if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { - _send_response(); - _clear_client(); - return; - } - - int read = 0; - ERR_FAIL_COND(req_pos >= 4096); - Error err = peer->get_partial_data(&req_buf[req_pos], 1, read); - if (err != OK) { - // Got an error - _clear_client(); - return; - } else if (read != 1) { - // Busy, wait next poll - return; - } - req_pos += read; - } - } -}; - -class EditorExportPlatformJavaScript : public EditorExportPlatform { - GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform); - - Ref<ImageTexture> logo; - Ref<ImageTexture> run_icon; - Ref<ImageTexture> stop_icon; - int menu_options = 0; - - Ref<EditorHTTPServer> server; - bool server_quit = false; - Mutex server_lock; - Thread server_thread; - - enum ExportMode { - EXPORT_MODE_NORMAL = 0, - EXPORT_MODE_THREADS = 1, - EXPORT_MODE_GDNATIVE = 2, - }; - - String _get_template_name(ExportMode p_mode, bool p_debug) const { - String name = "webassembly"; - switch (p_mode) { - case EXPORT_MODE_THREADS: - name += "_threads"; - break; - case EXPORT_MODE_GDNATIVE: - name += "_gdnative"; - break; - default: - break; - } - if (p_debug) { - name += "_debug.zip"; - } else { - name += "_release.zip"; - } - return name; - } - - Ref<Image> _get_project_icon() const { - Ref<Image> icon; - icon.instantiate(); - const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges(); - if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) { - return EditorNode::get_singleton()->get_editor_theme()->get_icon("DefaultProjectIcon", "EditorIcons")->get_image(); - } - return icon; - } - - Ref<Image> _get_project_splash() const { - Ref<Image> splash; - splash.instantiate(); - const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges(); - if (splash_path.is_empty() || ImageLoader::load_image(splash_path, splash) != OK) { - return Ref<Image>(memnew(Image(boot_splash_png))); - } - return splash; - } - - Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa); - void _replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template); - void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); - Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr); - Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects); - Error _write_or_error(const uint8_t *p_content, int p_len, String p_path); - - static void _server_thread_poll(void *data); - -public: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - - virtual void get_export_options(List<ExportOption> *r_options) override; - - virtual String get_name() const override; - virtual String get_os_name() const override; - virtual Ref<Texture2D> get_logo() const override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool poll_export() override; - virtual int get_options_count() const override; - virtual String get_option_label(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); } - virtual String get_option_tooltip(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); } - virtual Ref<ImageTexture> get_option_icon(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override; - virtual Ref<Texture2D> get_run_icon() const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("web"); - r_features->push_back(get_os_name()); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - String get_debug_protocol() const override { return "ws://"; } - - EditorExportPlatformJavaScript(); - ~EditorExportPlatformJavaScript(); -}; - -Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) { - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io); - - if (!pkg) { - EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + p_template); - return ERR_FILE_NOT_FOUND; - } - - if (unzGoToFirstFile(pkg) != UNZ_OK) { - EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + p_template); - unzClose(pkg); - return ERR_FILE_CORRUPT; - } - - do { - //get filename - unz_file_info info; - char fname[16384]; - unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); - - String file = fname; - - // Skip service worker and offline page if not exporting pwa. - if (!pwa && (file == "godot.service.worker.js" || file == "godot.offline.html")) { - continue; - } - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - - //write - String dst = p_dir.plus_file(file.replace("godot", p_name)); - FileAccess *f = FileAccess::open(dst, FileAccess::WRITE); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst); - unzClose(pkg); - return ERR_FILE_CANT_WRITE; - } - f->store_buffer(data.ptr(), data.size()); - memdelete(f); - - } while (unzGoToNextFile(pkg) == UNZ_OK); - unzClose(pkg); - return OK; -} - -Error EditorExportPlatformJavaScript::_write_or_error(const uint8_t *p_content, int p_size, String p_path) { - FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path); - return ERR_FILE_CANT_WRITE; - } - f->store_buffer(p_content, p_size); - memdelete(f); - return OK; -} - -void EditorExportPlatformJavaScript::_replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template) { - String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size()); - String out; - Vector<String> lines = str_template.split("\n"); - for (int i = 0; i < lines.size(); i++) { - String current_line = lines[i]; - for (Map<String, String>::Element *E = p_replaces.front(); E; E = E->next()) { - current_line = current_line.replace(E->key(), E->get()); - } - out += current_line + "\n"; - } - CharString cs = out.utf8(); - r_template.resize(cs.length()); - for (int i = 0; i < cs.length(); i++) { - r_template.write[i] = cs[i]; - } -} - -void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { - // Engine.js config - Dictionary config; - Array libs; - for (int i = 0; i < p_shared_objects.size(); i++) { - libs.push_back(p_shared_objects[i].path.get_file()); - } - Vector<String> flags; - gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT)); - Array args; - for (int i = 0; i < flags.size(); i++) { - args.push_back(flags[i]); - } - config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy"); - config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard"); - config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start"); - config["gdnativeLibs"] = libs; - config["executable"] = p_name; - config["args"] = args; - config["fileSizes"] = p_file_sizes; - - String head_include; - if (p_preset->get("html/export_icon")) { - head_include += "<link id='-gd-engine-icon' rel='icon' type='image/png' href='" + p_name + ".icon.png' />\n"; - head_include += "<link rel='apple-touch-icon' href='" + p_name + ".apple-touch-icon.png'/>\n"; - } - if (p_preset->get("progressive_web_app/enabled")) { - head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n"; - head_include += "<script type='application/javascript'>window.addEventListener('load', () => {if ('serviceWorker' in navigator) {navigator.serviceWorker.register('" + - p_name + ".service.worker.js');}});</script>\n"; - } - - // Replaces HTML string - const String str_config = Variant(config).to_json_string(); - const String custom_head_include = p_preset->get("html/head_include"); - Map<String, String> replaces; - replaces["$GODOT_URL"] = p_name + ".js"; - replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name"); - replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include; - replaces["$GODOT_CONFIG"] = str_config; - _replace_strings(replaces, p_html); -} - -Error EditorExportPlatformJavaScript::_add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr) { - const String name = p_path.get_file().get_basename(); - const String icon_name = vformat("%s.%dx%d.png", name, p_size, p_size); - const String icon_dest = p_path.get_base_dir().plus_file(icon_name); - - Ref<Image> icon; - if (!p_icon.is_empty()) { - icon.instantiate(); - const Error err = ImageLoader::load_image(p_icon, icon); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + p_icon); - return err; - } - if (icon->get_width() != p_size || icon->get_height() != p_size) { - icon->resize(p_size, p_size); - } - } else { - icon = _get_project_icon(); - icon->resize(p_size, p_size); - } - const Error err = icon->save_png(icon_dest); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + icon_dest); - return err; - } - Dictionary icon_dict; - icon_dict["sizes"] = vformat("%dx%d", p_size, p_size); - icon_dict["type"] = "image/png"; - icon_dict["src"] = icon_name; - r_arr.push_back(icon_dict); - return err; -} - -Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) { - // Service worker - const String dir = p_path.get_base_dir(); - const String name = p_path.get_file().get_basename(); - const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - Map<String, String> replaces; - replaces["@GODOT_VERSION@"] = "1"; - replaces["@GODOT_NAME@"] = name; - replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html"; - Array files; - replaces["@GODOT_OPT_CACHE@"] = Variant(files).to_json_string(); - files.push_back(name + ".html"); - files.push_back(name + ".js"); - files.push_back(name + ".wasm"); - files.push_back(name + ".pck"); - files.push_back(name + ".offline.html"); - if (p_preset->get("html/export_icon")) { - files.push_back(name + ".icon.png"); - files.push_back(name + ".apple-touch-icon.png"); - } - if (mode == EXPORT_MODE_THREADS) { - files.push_back(name + ".worker.js"); - files.push_back(name + ".audio.worklet.js"); - } else if (mode == EXPORT_MODE_GDNATIVE) { - files.push_back(name + ".side.wasm"); - for (int i = 0; i < p_shared_objects.size(); i++) { - files.push_back(p_shared_objects[i].path.get_file()); - } - } - replaces["@GODOT_CACHE@"] = Variant(files).to_json_string(); - - const String sw_path = dir.plus_file(name + ".service.worker.js"); - Vector<uint8_t> sw; - { - FileAccess *f = FileAccess::open(sw_path, FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path); - return ERR_FILE_CANT_READ; - } - sw.resize(f->get_length()); - f->get_buffer(sw.ptrw(), sw.size()); - memdelete(f); - f = nullptr; - } - _replace_strings(replaces, sw); - Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js")); - if (err != OK) { - return err; - } - - // Custom offline page - const String offline_page = p_preset->get("progressive_web_app/offline_page"); - if (!offline_page.is_empty()) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - const String offline_dest = dir.plus_file(name + ".offline.html"); - err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + offline_dest); - return err; - } - } - - // Manifest - const char *modes[4] = { "fullscreen", "standalone", "minimal-ui", "browser" }; - const char *orientations[3] = { "any", "landscape", "portrait" }; - const int display = CLAMP(int(p_preset->get("progressive_web_app/display")), 0, 4); - const int orientation = CLAMP(int(p_preset->get("progressive_web_app/orientation")), 0, 3); - - Dictionary manifest; - String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name"); - if (proj_name.is_empty()) { - proj_name = "Godot Game"; - } - manifest["name"] = proj_name; - manifest["start_url"] = "./" + name + ".html"; - manifest["display"] = String::utf8(modes[display]); - manifest["orientation"] = String::utf8(orientations[orientation]); - manifest["background_color"] = "#" + p_preset->get("progressive_web_app/background_color").operator Color().to_html(false); - - Array icons_arr; - const String icon144_path = p_preset->get("progressive_web_app/icon_144x144"); - err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr); - if (err != OK) { - return err; - } - const String icon180_path = p_preset->get("progressive_web_app/icon_180x180"); - err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr); - if (err != OK) { - return err; - } - const String icon512_path = p_preset->get("progressive_web_app/icon_512x512"); - err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr); - if (err != OK) { - return err; - } - manifest["icons"] = icons_arr; - - CharString cs = Variant(manifest).to_json_string().utf8(); - err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.plus_file(name + ".manifest.json")); - if (err != OK) { - return err; - } - - return OK; -} - -void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - if (p_preset->get("vram_texture_compression/for_desktop")) { - r_features->push_back("s3tc"); - } - - if (p_preset->get("vram_texture_compression/for_mobile")) { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - if (driver == "GLES2") { - r_features->push_back("etc"); - } else if (driver == "Vulkan") { - // FIXME: Review if this is correct. - r_features->push_back("etc2"); - } - } - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - if (mode == EXPORT_MODE_THREADS) { - r_features->push_back("threads"); - } else if (mode == EXPORT_MODE_GDNATIVE) { - r_features->push_back("wasm32"); - } -} - -void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type. - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/export_icon"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color())); -} - -String EditorExportPlatformJavaScript::get_name() const { - return "HTML5"; -} - -String EditorExportPlatformJavaScript::get_os_name() const { - return "HTML5"; -} - -Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const { - return logo; -} - -bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - - // Look for export templates (first official, and if defined custom templates). - bool dvalid = exists_export_template(_get_template_name(mode, true), &err); - bool rvalid = exists_export_template(_get_template_name(mode, false), &err); - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - // Validate the rest of the configuration. - - if (p_preset->get("vram_texture_compression/for_mobile")) { - String etc_error = test_etc2(); - if (etc_error != String()) { - valid = false; - err += etc_error; - } - } - - if (!err.is_empty()) { - r_error = err; - } - - return valid; -} - -List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { - List<String> list; - list.push_back("html"); - return list; -} - -Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - const String custom_debug = p_preset->get("custom_template/debug"); - const String custom_release = p_preset->get("custom_template/release"); - const String custom_html = p_preset->get("html/custom_html_shell"); - const bool export_icon = p_preset->get("html/export_icon"); - const bool pwa = p_preset->get("progressive_web_app/enabled"); - - const String base_dir = p_path.get_base_dir(); - const String base_path = p_path.get_basename(); - const String base_name = p_path.get_file().get_basename(); - - // Find the correct template - String template_path = p_debug ? custom_debug : custom_release; - template_path = template_path.strip_edges(); - if (template_path == String()) { - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - template_path = find_export_template(_get_template_name(mode, p_debug)); - } - - if (!DirAccess::exists(base_dir)) { - return ERR_FILE_BAD_PATH; - } - - if (template_path != String() && !FileAccess::exists(template_path)) { - EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path); - return ERR_FILE_NOT_FOUND; - } - - // Export pck and shared objects - Vector<SharedObject> shared_objects; - String pck_path = base_path + ".pck"; - Error error = save_pack(p_preset, pck_path, &shared_objects); - if (error != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path); - return error; - } - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < shared_objects.size(); i++) { - String dst = base_dir.plus_file(shared_objects[i].path.get_file()); - error = da->copy(shared_objects[i].path, dst); - if (error != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file()); - memdelete(da); - return error; - } - } - memdelete(da); - da = nullptr; - - // Extract templates. - error = _extract_template(template_path, base_dir, base_name, pwa); - if (error) { - return error; - } - - // Parse generated file sizes (pck and wasm, to help show a meaningful loading bar). - Dictionary file_sizes; - FileAccess *f = nullptr; - f = FileAccess::open(pck_path, FileAccess::READ); - if (f) { - file_sizes[pck_path.get_file()] = (uint64_t)f->get_length(); - memdelete(f); - f = nullptr; - } - f = FileAccess::open(base_path + ".wasm", FileAccess::READ); - if (f) { - file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length(); - memdelete(f); - f = nullptr; - } - - // Read the HTML shell file (custom or from template). - const String html_path = custom_html.is_empty() ? base_path + ".html" : custom_html; - Vector<uint8_t> html; - f = FileAccess::open(html_path, FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path); - return ERR_FILE_CANT_READ; - } - html.resize(f->get_length()); - f->get_buffer(html.ptrw(), html.size()); - memdelete(f); - f = nullptr; - - // Generate HTML file with replaced strings. - _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); - Error err = _write_or_error(html.ptr(), html.size(), p_path); - if (err != OK) { - return err; - } - html.resize(0); - - // Export splash (why?) - Ref<Image> splash = _get_project_splash(); - const String splash_png_path = base_path + ".png"; - if (splash->save_png(splash_png_path) != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + splash_png_path); - return ERR_FILE_CANT_WRITE; - } - - // Save a favicon that can be accessed without waiting for the project to finish loading. - // This way, the favicon can be displayed immediately when loading the page. - if (export_icon) { - Ref<Image> favicon = _get_project_icon(); - const String favicon_png_path = base_path + ".icon.png"; - if (favicon->save_png(favicon_png_path) != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + favicon_png_path); - return ERR_FILE_CANT_WRITE; - } - favicon->resize(180, 180); - const String apple_icon_png_path = base_path + ".apple-touch-icon.png"; - if (favicon->save_png(apple_icon_png_path) != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + apple_icon_png_path); - return ERR_FILE_CANT_WRITE; - } - } - - // Generate the PWA worker and manifest - if (pwa) { - err = _build_pwa(p_preset, p_path, shared_objects); - if (err != OK) { - return err; - } - } - - return OK; -} - -bool EditorExportPlatformJavaScript::poll_export() { - Ref<EditorExportPreset> preset; - - for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { - Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i); - if (ep->is_runnable() && ep->get_platform() == this) { - preset = ep; - break; - } - } - - int prev = menu_options; - menu_options = preset.is_valid(); - if (server->is_listening()) { - if (menu_options == 0) { - MutexLock lock(server_lock); - server->stop(); - } else { - menu_options += 1; - } - } - return menu_options != prev; -} - -Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) const { - return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index); -} - -int EditorExportPlatformJavaScript::get_options_count() const { - return menu_options; -} - -Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) { - if (p_option == 1) { - MutexLock lock(server_lock); - server->stop(); - return OK; - } - - const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (!da->dir_exists(dest)) { - Error err = da->make_dir_recursive(dest); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not create HTTP server directory:") + "\n" + dest); - return err; - } - } - const String basepath = dest.plus_file("tmp_js_export"); - Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags); - if (err != OK) { - // Export generates several files, clean them up on failure. - DirAccess::remove_file_or_error(basepath + ".html"); - DirAccess::remove_file_or_error(basepath + ".offline.html"); - DirAccess::remove_file_or_error(basepath + ".js"); - DirAccess::remove_file_or_error(basepath + ".worker.js"); - DirAccess::remove_file_or_error(basepath + ".audio.worklet.js"); - DirAccess::remove_file_or_error(basepath + ".service.worker.js"); - DirAccess::remove_file_or_error(basepath + ".pck"); - DirAccess::remove_file_or_error(basepath + ".png"); - DirAccess::remove_file_or_error(basepath + ".side.wasm"); - DirAccess::remove_file_or_error(basepath + ".wasm"); - DirAccess::remove_file_or_error(basepath + ".icon.png"); - DirAccess::remove_file_or_error(basepath + ".apple-touch-icon.png"); - return err; - } - - const uint16_t bind_port = EDITOR_GET("export/web/http_port"); - // Resolve host if needed. - const String bind_host = EDITOR_GET("export/web/http_host"); - IPAddress bind_ip; - if (bind_host.is_valid_ip_address()) { - bind_ip = bind_host; - } else { - bind_ip = IP::get_singleton()->resolve_hostname(bind_host); - } - ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'."); - - const bool use_ssl = EDITOR_GET("export/web/use_ssl"); - const String ssl_key = EDITOR_GET("export/web/ssl_key"); - const String ssl_cert = EDITOR_GET("export/web/ssl_certificate"); - - // Restart server. - { - MutexLock lock(server_lock); - - server->stop(); - err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert); - } - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Error starting HTTP server:") + "\n" + itos(err)); - return err; - } - - OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); - // FIXME: Find out how to clean up export files after running the successfully - // exported game. Might not be trivial. - return OK; -} - -Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const { - return run_icon; -} - -void EditorExportPlatformJavaScript::_server_thread_poll(void *data) { - EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data; - while (!ej->server_quit) { - OS::get_singleton()->delay_usec(1000); - { - MutexLock lock(ej->server_lock); - ej->server->poll(); - } - } -} - -EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { - server.instantiate(); - server_thread.start(_server_thread_poll, this); - - Ref<Image> img = memnew(Image(_javascript_logo)); - logo.instantiate(); - logo->create_from_image(img); - - img = Ref<Image>(memnew(Image(_javascript_run_icon))); - run_icon.instantiate(); - run_icon->create_from_image(img); - - Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); - if (theme.is_valid()) { - stop_icon = theme->get_icon("Stop", "EditorIcons"); - } else { - stop_icon.instantiate(); - } -} - -EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { - server->stop(); - server_quit = true; - server_thread.wait_to_finish(); -} +#include "export_plugin.h" void register_javascript_exporter() { EDITOR_DEF("export/web/http_host", "localhost"); diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h index e641339f55..8e8161b547 100644 --- a/platform/javascript/export/export.h +++ b/platform/javascript/export/export.h @@ -28,4 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef JAVASCRIPT_EXPORT_H +#define JAVASCRIPT_EXPORT_H + void register_javascript_exporter(); + +#endif diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp new file mode 100644 index 0000000000..5d285a6e60 --- /dev/null +++ b/platform/javascript/export/export_plugin.cpp @@ -0,0 +1,671 @@ +/*************************************************************************/ +/* export_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 "export_plugin.h" + +Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) { + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io); + + if (!pkg) { + EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + p_template); + return ERR_FILE_NOT_FOUND; + } + + if (unzGoToFirstFile(pkg) != UNZ_OK) { + EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + p_template); + unzClose(pkg); + return ERR_FILE_CORRUPT; + } + + do { + //get filename + unz_file_info info; + char fname[16384]; + unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String file = fname; + + // Skip service worker and offline page if not exporting pwa. + if (!pwa && (file == "godot.service.worker.js" || file == "godot.offline.html")) { + continue; + } + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + + //write + String dst = p_dir.plus_file(file.replace("godot", p_name)); + FileAccess *f = FileAccess::open(dst, FileAccess::WRITE); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst); + unzClose(pkg); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(data.ptr(), data.size()); + memdelete(f); + + } while (unzGoToNextFile(pkg) == UNZ_OK); + unzClose(pkg); + return OK; +} + +Error EditorExportPlatformJavaScript::_write_or_error(const uint8_t *p_content, int p_size, String p_path) { + FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(p_content, p_size); + memdelete(f); + return OK; +} + +void EditorExportPlatformJavaScript::_replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template) { + String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size()); + String out; + Vector<String> lines = str_template.split("\n"); + for (int i = 0; i < lines.size(); i++) { + String current_line = lines[i]; + for (Map<String, String>::Element *E = p_replaces.front(); E; E = E->next()) { + current_line = current_line.replace(E->key(), E->get()); + } + out += current_line + "\n"; + } + CharString cs = out.utf8(); + r_template.resize(cs.length()); + for (int i = 0; i < cs.length(); i++) { + r_template.write[i] = cs[i]; + } +} + +void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { + // Engine.js config + Dictionary config; + Array libs; + for (int i = 0; i < p_shared_objects.size(); i++) { + libs.push_back(p_shared_objects[i].path.get_file()); + } + Vector<String> flags; + gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT)); + Array args; + for (int i = 0; i < flags.size(); i++) { + args.push_back(flags[i]); + } + config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy"); + config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard"); + config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start"); + config["gdnativeLibs"] = libs; + config["executable"] = p_name; + config["args"] = args; + config["fileSizes"] = p_file_sizes; + + String head_include; + if (p_preset->get("html/export_icon")) { + head_include += "<link id='-gd-engine-icon' rel='icon' type='image/png' href='" + p_name + ".icon.png' />\n"; + head_include += "<link rel='apple-touch-icon' href='" + p_name + ".apple-touch-icon.png'/>\n"; + } + if (p_preset->get("progressive_web_app/enabled")) { + head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n"; + head_include += "<script type='application/javascript'>window.addEventListener('load', () => {if ('serviceWorker' in navigator) {navigator.serviceWorker.register('" + + p_name + ".service.worker.js');}});</script>\n"; + } + + // Replaces HTML string + const String str_config = Variant(config).to_json_string(); + const String custom_head_include = p_preset->get("html/head_include"); + Map<String, String> replaces; + replaces["$GODOT_URL"] = p_name + ".js"; + replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name"); + replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include; + replaces["$GODOT_CONFIG"] = str_config; + _replace_strings(replaces, p_html); +} + +Error EditorExportPlatformJavaScript::_add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr) { + const String name = p_path.get_file().get_basename(); + const String icon_name = vformat("%s.%dx%d.png", name, p_size, p_size); + const String icon_dest = p_path.get_base_dir().plus_file(icon_name); + + Ref<Image> icon; + if (!p_icon.is_empty()) { + icon.instantiate(); + const Error err = ImageLoader::load_image(p_icon, icon); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + p_icon); + return err; + } + if (icon->get_width() != p_size || icon->get_height() != p_size) { + icon->resize(p_size, p_size); + } + } else { + icon = _get_project_icon(); + icon->resize(p_size, p_size); + } + const Error err = icon->save_png(icon_dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + icon_dest); + return err; + } + Dictionary icon_dict; + icon_dict["sizes"] = vformat("%dx%d", p_size, p_size); + icon_dict["type"] = "image/png"; + icon_dict["src"] = icon_name; + r_arr.push_back(icon_dict); + return err; +} + +Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) { + // Service worker + const String dir = p_path.get_base_dir(); + const String name = p_path.get_file().get_basename(); + const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + Map<String, String> replaces; + replaces["@GODOT_VERSION@"] = "1"; + replaces["@GODOT_NAME@"] = name; + replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html"; + Array files; + replaces["@GODOT_OPT_CACHE@"] = Variant(files).to_json_string(); + files.push_back(name + ".html"); + files.push_back(name + ".js"); + files.push_back(name + ".wasm"); + files.push_back(name + ".pck"); + files.push_back(name + ".offline.html"); + if (p_preset->get("html/export_icon")) { + files.push_back(name + ".icon.png"); + files.push_back(name + ".apple-touch-icon.png"); + } + if (mode == EXPORT_MODE_THREADS) { + files.push_back(name + ".worker.js"); + files.push_back(name + ".audio.worklet.js"); + } else if (mode == EXPORT_MODE_GDNATIVE) { + files.push_back(name + ".side.wasm"); + for (int i = 0; i < p_shared_objects.size(); i++) { + files.push_back(p_shared_objects[i].path.get_file()); + } + } + replaces["@GODOT_CACHE@"] = Variant(files).to_json_string(); + + const String sw_path = dir.plus_file(name + ".service.worker.js"); + Vector<uint8_t> sw; + { + FileAccess *f = FileAccess::open(sw_path, FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path); + return ERR_FILE_CANT_READ; + } + sw.resize(f->get_length()); + f->get_buffer(sw.ptrw(), sw.size()); + memdelete(f); + f = nullptr; + } + _replace_strings(replaces, sw); + Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js")); + if (err != OK) { + return err; + } + + // Custom offline page + const String offline_page = p_preset->get("progressive_web_app/offline_page"); + if (!offline_page.is_empty()) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + const String offline_dest = dir.plus_file(name + ".offline.html"); + err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + offline_dest); + return err; + } + } + + // Manifest + const char *modes[4] = { "fullscreen", "standalone", "minimal-ui", "browser" }; + const char *orientations[3] = { "any", "landscape", "portrait" }; + const int display = CLAMP(int(p_preset->get("progressive_web_app/display")), 0, 4); + const int orientation = CLAMP(int(p_preset->get("progressive_web_app/orientation")), 0, 3); + + Dictionary manifest; + String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name"); + if (proj_name.is_empty()) { + proj_name = "Godot Game"; + } + manifest["name"] = proj_name; + manifest["start_url"] = "./" + name + ".html"; + manifest["display"] = String::utf8(modes[display]); + manifest["orientation"] = String::utf8(orientations[orientation]); + manifest["background_color"] = "#" + p_preset->get("progressive_web_app/background_color").operator Color().to_html(false); + + Array icons_arr; + const String icon144_path = p_preset->get("progressive_web_app/icon_144x144"); + err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr); + if (err != OK) { + return err; + } + const String icon180_path = p_preset->get("progressive_web_app/icon_180x180"); + err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr); + if (err != OK) { + return err; + } + const String icon512_path = p_preset->get("progressive_web_app/icon_512x512"); + err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr); + if (err != OK) { + return err; + } + manifest["icons"] = icons_arr; + + CharString cs = Variant(manifest).to_json_string().utf8(); + err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.plus_file(name + ".manifest.json")); + if (err != OK) { + return err; + } + + return OK; +} + +void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + if (p_preset->get("vram_texture_compression/for_desktop")) { + r_features->push_back("s3tc"); + } + + if (p_preset->get("vram_texture_compression/for_mobile")) { + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } else if (driver == "Vulkan") { + // FIXME: Review if this is correct. + r_features->push_back("etc2"); + } + } + ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + if (mode == EXPORT_MODE_THREADS) { + r_features->push_back("threads"); + } else if (mode == EXPORT_MODE_GDNATIVE) { + r_features->push_back("wasm32"); + } +} + +void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type. + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/export_icon"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color())); +} + +String EditorExportPlatformJavaScript::get_name() const { + return "HTML5"; +} + +String EditorExportPlatformJavaScript::get_os_name() const { + return "HTML5"; +} + +Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const { + return logo; +} + +bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + + // Look for export templates (first official, and if defined custom templates). + bool dvalid = exists_export_template(_get_template_name(mode, true), &err); + bool rvalid = exists_export_template(_get_template_name(mode, false), &err); + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + // Validate the rest of the configuration. + + if (p_preset->get("vram_texture_compression/for_mobile")) { + String etc_error = test_etc2(); + if (etc_error != String()) { + valid = false; + err += etc_error; + } + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("html"); + return list; +} + +Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + const String custom_debug = p_preset->get("custom_template/debug"); + const String custom_release = p_preset->get("custom_template/release"); + const String custom_html = p_preset->get("html/custom_html_shell"); + const bool export_icon = p_preset->get("html/export_icon"); + const bool pwa = p_preset->get("progressive_web_app/enabled"); + + const String base_dir = p_path.get_base_dir(); + const String base_path = p_path.get_basename(); + const String base_name = p_path.get_file().get_basename(); + + // Find the correct template + String template_path = p_debug ? custom_debug : custom_release; + template_path = template_path.strip_edges(); + if (template_path == String()) { + ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + template_path = find_export_template(_get_template_name(mode, p_debug)); + } + + if (!DirAccess::exists(base_dir)) { + return ERR_FILE_BAD_PATH; + } + + if (template_path != String() && !FileAccess::exists(template_path)) { + EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path); + return ERR_FILE_NOT_FOUND; + } + + // Export pck and shared objects + Vector<SharedObject> shared_objects; + String pck_path = base_path + ".pck"; + Error error = save_pack(p_preset, pck_path, &shared_objects); + if (error != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path); + return error; + } + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < shared_objects.size(); i++) { + String dst = base_dir.plus_file(shared_objects[i].path.get_file()); + error = da->copy(shared_objects[i].path, dst); + if (error != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file()); + memdelete(da); + return error; + } + } + memdelete(da); + da = nullptr; + + // Extract templates. + error = _extract_template(template_path, base_dir, base_name, pwa); + if (error) { + return error; + } + + // Parse generated file sizes (pck and wasm, to help show a meaningful loading bar). + Dictionary file_sizes; + FileAccess *f = nullptr; + f = FileAccess::open(pck_path, FileAccess::READ); + if (f) { + file_sizes[pck_path.get_file()] = (uint64_t)f->get_length(); + memdelete(f); + f = nullptr; + } + f = FileAccess::open(base_path + ".wasm", FileAccess::READ); + if (f) { + file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length(); + memdelete(f); + f = nullptr; + } + + // Read the HTML shell file (custom or from template). + const String html_path = custom_html.is_empty() ? base_path + ".html" : custom_html; + Vector<uint8_t> html; + f = FileAccess::open(html_path, FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path); + return ERR_FILE_CANT_READ; + } + html.resize(f->get_length()); + f->get_buffer(html.ptrw(), html.size()); + memdelete(f); + f = nullptr; + + // Generate HTML file with replaced strings. + _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); + Error err = _write_or_error(html.ptr(), html.size(), p_path); + if (err != OK) { + return err; + } + html.resize(0); + + // Export splash (why?) + Ref<Image> splash = _get_project_splash(); + const String splash_png_path = base_path + ".png"; + if (splash->save_png(splash_png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + splash_png_path); + return ERR_FILE_CANT_WRITE; + } + + // Save a favicon that can be accessed without waiting for the project to finish loading. + // This way, the favicon can be displayed immediately when loading the page. + if (export_icon) { + Ref<Image> favicon = _get_project_icon(); + const String favicon_png_path = base_path + ".icon.png"; + if (favicon->save_png(favicon_png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + favicon_png_path); + return ERR_FILE_CANT_WRITE; + } + favicon->resize(180, 180); + const String apple_icon_png_path = base_path + ".apple-touch-icon.png"; + if (favicon->save_png(apple_icon_png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + apple_icon_png_path); + return ERR_FILE_CANT_WRITE; + } + } + + // Generate the PWA worker and manifest + if (pwa) { + err = _build_pwa(p_preset, p_path, shared_objects); + if (err != OK) { + return err; + } + } + + return OK; +} + +bool EditorExportPlatformJavaScript::poll_export() { + Ref<EditorExportPreset> preset; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i); + if (ep->is_runnable() && ep->get_platform() == this) { + preset = ep; + break; + } + } + + int prev = menu_options; + menu_options = preset.is_valid(); + if (server->is_listening()) { + if (menu_options == 0) { + MutexLock lock(server_lock); + server->stop(); + } else { + menu_options += 1; + } + } + return menu_options != prev; +} + +Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) const { + return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index); +} + +int EditorExportPlatformJavaScript::get_options_count() const { + return menu_options; +} + +Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) { + if (p_option == 1) { + MutexLock lock(server_lock); + server->stop(); + return OK; + } + + const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (!da->dir_exists(dest)) { + Error err = da->make_dir_recursive(dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not create HTTP server directory:") + "\n" + dest); + return err; + } + } + const String basepath = dest.plus_file("tmp_js_export"); + Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags); + if (err != OK) { + // Export generates several files, clean them up on failure. + DirAccess::remove_file_or_error(basepath + ".html"); + DirAccess::remove_file_or_error(basepath + ".offline.html"); + DirAccess::remove_file_or_error(basepath + ".js"); + DirAccess::remove_file_or_error(basepath + ".worker.js"); + DirAccess::remove_file_or_error(basepath + ".audio.worklet.js"); + DirAccess::remove_file_or_error(basepath + ".service.worker.js"); + DirAccess::remove_file_or_error(basepath + ".pck"); + DirAccess::remove_file_or_error(basepath + ".png"); + DirAccess::remove_file_or_error(basepath + ".side.wasm"); + DirAccess::remove_file_or_error(basepath + ".wasm"); + DirAccess::remove_file_or_error(basepath + ".icon.png"); + DirAccess::remove_file_or_error(basepath + ".apple-touch-icon.png"); + return err; + } + + const uint16_t bind_port = EDITOR_GET("export/web/http_port"); + // Resolve host if needed. + const String bind_host = EDITOR_GET("export/web/http_host"); + IPAddress bind_ip; + if (bind_host.is_valid_ip_address()) { + bind_ip = bind_host; + } else { + bind_ip = IP::get_singleton()->resolve_hostname(bind_host); + } + ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'."); + + const bool use_ssl = EDITOR_GET("export/web/use_ssl"); + const String ssl_key = EDITOR_GET("export/web/ssl_key"); + const String ssl_cert = EDITOR_GET("export/web/ssl_certificate"); + + // Restart server. + { + MutexLock lock(server_lock); + + server->stop(); + err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert); + } + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Error starting HTTP server:") + "\n" + itos(err)); + return err; + } + + OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); + // FIXME: Find out how to clean up export files after running the successfully + // exported game. Might not be trivial. + return OK; +} + +Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const { + return run_icon; +} + +void EditorExportPlatformJavaScript::_server_thread_poll(void *data) { + EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data; + while (!ej->server_quit) { + OS::get_singleton()->delay_usec(1000); + { + MutexLock lock(ej->server_lock); + ej->server->poll(); + } + } +} + +EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + server.instantiate(); + server_thread.start(_server_thread_poll, this); + + Ref<Image> img = memnew(Image(_javascript_logo)); + logo.instantiate(); + logo->create_from_image(img); + + img = Ref<Image>(memnew(Image(_javascript_run_icon))); + run_icon.instantiate(); + run_icon->create_from_image(img); + + Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); + if (theme.is_valid()) { + stop_icon = theme->get_icon("Stop", "EditorIcons"); + } else { + stop_icon.instantiate(); + } +} + +EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + server->stop(); + server_quit = true; + server_thread.wait_to_finish(); +} diff --git a/platform/javascript/export/export_plugin.h b/platform/javascript/export/export_plugin.h new file mode 100644 index 0000000000..736edfe3a8 --- /dev/null +++ b/platform/javascript/export/export_plugin.h @@ -0,0 +1,149 @@ +/*************************************************************************/ +/* export_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 JAVASCRIPT_EXPORT_PLUGIN_H +#define JAVASCRIPT_EXPORT_PLUGIN_H + +#include "core/io/image_loader.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/tcp_server.h" +#include "core/io/zip_io.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "main/splash.gen.h" +#include "platform/javascript/logo.gen.h" +#include "platform/javascript/run_icon.gen.h" + +#include "export_server.h" + +class EditorExportPlatformJavaScript : public EditorExportPlatform { + GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform); + + Ref<ImageTexture> logo; + Ref<ImageTexture> run_icon; + Ref<ImageTexture> stop_icon; + int menu_options = 0; + + Ref<EditorHTTPServer> server; + bool server_quit = false; + Mutex server_lock; + Thread server_thread; + + enum ExportMode { + EXPORT_MODE_NORMAL = 0, + EXPORT_MODE_THREADS = 1, + EXPORT_MODE_GDNATIVE = 2, + }; + + String _get_template_name(ExportMode p_mode, bool p_debug) const { + String name = "webassembly"; + switch (p_mode) { + case EXPORT_MODE_THREADS: + name += "_threads"; + break; + case EXPORT_MODE_GDNATIVE: + name += "_gdnative"; + break; + default: + break; + } + if (p_debug) { + name += "_debug.zip"; + } else { + name += "_release.zip"; + } + return name; + } + + Ref<Image> _get_project_icon() const { + Ref<Image> icon; + icon.instantiate(); + const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges(); + if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) { + return EditorNode::get_singleton()->get_editor_theme()->get_icon("DefaultProjectIcon", "EditorIcons")->get_image(); + } + return icon; + } + + Ref<Image> _get_project_splash() const { + Ref<Image> splash; + splash.instantiate(); + const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges(); + if (splash_path.is_empty() || ImageLoader::load_image(splash_path, splash) != OK) { + return Ref<Image>(memnew(Image(boot_splash_png))); + } + return splash; + } + + Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa); + void _replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template); + void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); + Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr); + Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects); + Error _write_or_error(const uint8_t *p_content, int p_len, String p_path); + + static void _server_thread_poll(void *data); + +public: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + + virtual void get_export_options(List<ExportOption> *r_options) override; + + virtual String get_name() const override; + virtual String get_os_name() const override; + virtual Ref<Texture2D> get_logo() const override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual bool poll_export() override; + virtual int get_options_count() const override; + virtual String get_option_label(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); } + virtual String get_option_tooltip(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); } + virtual Ref<ImageTexture> get_option_icon(int p_index) const override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override; + virtual Ref<Texture2D> get_run_icon() const override; + + virtual void get_platform_features(List<String> *r_features) override { + r_features->push_back("web"); + r_features->push_back(get_os_name()); + } + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { + } + + String get_debug_protocol() const override { return "ws://"; } + + EditorExportPlatformJavaScript(); + ~EditorExportPlatformJavaScript(); +}; + +#endif diff --git a/platform/javascript/export/export_server.h b/platform/javascript/export/export_server.h new file mode 100644 index 0000000000..87cbdcab47 --- /dev/null +++ b/platform/javascript/export/export_server.h @@ -0,0 +1,254 @@ +/*************************************************************************/ +/* export_server.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 JAVASCRIPT_EXPORT_SERVER_H +#define JAVASCRIPT_EXPORT_SERVER_H + +#include "core/io/image_loader.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/tcp_server.h" +#include "core/io/zip_io.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" + +class EditorHTTPServer : public RefCounted { +private: + Ref<TCPServer> server; + Map<String, String> mimes; + Ref<StreamPeerTCP> tcp; + Ref<StreamPeerSSL> ssl; + Ref<StreamPeer> peer; + Ref<CryptoKey> key; + Ref<X509Certificate> cert; + bool use_ssl = false; + uint64_t time = 0; + uint8_t req_buf[4096]; + int req_pos = 0; + + void _clear_client() { + peer = Ref<StreamPeer>(); + ssl = Ref<StreamPeerSSL>(); + tcp = Ref<StreamPeerTCP>(); + memset(req_buf, 0, sizeof(req_buf)); + time = 0; + req_pos = 0; + } + + void _set_internal_certs(Ref<Crypto> p_crypto) { + const String cache_path = EditorPaths::get_singleton()->get_cache_dir(); + const String key_path = cache_path.plus_file("html5_server.key"); + const String crt_path = cache_path.plus_file("html5_server.crt"); + bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path); + if (!regen) { + key = Ref<CryptoKey>(CryptoKey::create()); + cert = Ref<X509Certificate>(X509Certificate::create()); + if (key->load(key_path) != OK || cert->load(crt_path) != OK) { + regen = true; + } + } + if (regen) { + key = p_crypto->generate_rsa(2048); + key->save(key_path); + cert = p_crypto->generate_self_signed_certificate(key, "CN=godot-debug.local,O=A Game Dev,C=XXA", "20140101000000", "20340101000000"); + cert->save(crt_path); + } + } + +public: + EditorHTTPServer() { + mimes["html"] = "text/html"; + mimes["js"] = "application/javascript"; + mimes["json"] = "application/json"; + mimes["pck"] = "application/octet-stream"; + mimes["png"] = "image/png"; + mimes["svg"] = "image/svg"; + mimes["wasm"] = "application/wasm"; + server.instantiate(); + stop(); + } + + void stop() { + server->stop(); + _clear_client(); + } + + Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) { + use_ssl = p_use_ssl; + if (use_ssl) { + Ref<Crypto> crypto = Crypto::create(); + if (crypto.is_null()) { + return ERR_UNAVAILABLE; + } + if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) { + key = Ref<CryptoKey>(CryptoKey::create()); + Error err = key->load(p_ssl_key); + ERR_FAIL_COND_V(err != OK, err); + cert = Ref<X509Certificate>(X509Certificate::create()); + err = cert->load(p_ssl_cert); + ERR_FAIL_COND_V(err != OK, err); + } else { + _set_internal_certs(crypto); + } + } + return server->listen(p_port, p_address); + } + + bool is_listening() const { + return server->is_listening(); + } + + void _send_response() { + Vector<String> psa = String((char *)req_buf).split("\r\n"); + int len = psa.size(); + ERR_FAIL_COND_MSG(len < 4, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); + + Vector<String> req = psa[0].split(" ", false); + ERR_FAIL_COND_MSG(req.size() < 2, "Invalid protocol or status code."); + + // Wrong protocol + ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version."); + + const int query_index = req[1].find_char('?'); + const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index); + + const String req_file = path.get_file(); + const String req_ext = path.get_extension(); + const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); + const String filepath = cache_path.plus_file(req_file); + + if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) { + String s = "HTTP/1.1 404 Not Found\r\n"; + s += "Connection: Close\r\n"; + s += "\r\n"; + CharString cs = s.utf8(); + peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); + return; + } + const String ctype = mimes[req_ext]; + + FileAccess *f = FileAccess::open(filepath, FileAccess::READ); + ERR_FAIL_COND(!f); + String s = "HTTP/1.1 200 OK\r\n"; + s += "Connection: Close\r\n"; + s += "Content-Type: " + ctype + "\r\n"; + s += "Access-Control-Allow-Origin: *\r\n"; + s += "Cross-Origin-Opener-Policy: same-origin\r\n"; + s += "Cross-Origin-Embedder-Policy: require-corp\r\n"; + s += "Cache-Control: no-store, max-age=0\r\n"; + s += "\r\n"; + CharString cs = s.utf8(); + Error err = peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); + if (err != OK) { + memdelete(f); + ERR_FAIL(); + } + + while (true) { + uint8_t bytes[4096]; + uint64_t read = f->get_buffer(bytes, 4096); + if (read == 0) { + break; + } + err = peer->put_data(bytes, read); + if (err != OK) { + memdelete(f); + ERR_FAIL(); + } + } + memdelete(f); + } + + void poll() { + if (!server->is_listening()) { + return; + } + if (tcp.is_null()) { + if (!server->is_connection_available()) { + return; + } + tcp = server->take_connection(); + peer = tcp; + time = OS::get_singleton()->get_ticks_usec(); + } + if (OS::get_singleton()->get_ticks_usec() - time > 1000000) { + _clear_client(); + return; + } + if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + return; + } + + if (use_ssl) { + if (ssl.is_null()) { + ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + peer = ssl; + ssl->set_blocking_handshake_enabled(false); + if (ssl->accept_stream(tcp, key, cert) != OK) { + _clear_client(); + return; + } + } + ssl->poll(); + if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { + // Still handshaking, keep waiting. + return; + } + if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + _clear_client(); + return; + } + } + + while (true) { + char *r = (char *)req_buf; + int l = req_pos - 1; + if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { + _send_response(); + _clear_client(); + return; + } + + int read = 0; + ERR_FAIL_COND(req_pos >= 4096); + Error err = peer->get_partial_data(&req_buf[req_pos], 1, read); + if (err != OK) { + // Got an error + _clear_client(); + return; + } else if (read != 1) { + // Busy, wait next poll + return; + } + req_pos += read; + } + } +}; + +#endif diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 131c4b821e..1164d76580 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -30,1153 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/io/resource_saver.h" -#include "core/io/zip_io.h" -#include "core/os/os.h" -#include "core/version.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "platform/osx/logo.gen.h" - -#include <sys/stat.h> - -class EditorExportPlatformOSX : public EditorExportPlatform { - GDCLASS(EditorExportPlatformOSX, EditorExportPlatform); - - int version_code = 0; - - Ref<ImageTexture> logo; - - void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); - void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); - - Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path); - Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); - void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); - -#ifdef OSX_ENABLED - bool use_codesign() const { return true; } - bool use_dmg() const { return true; } -#else - bool use_codesign() const { return false; } - bool use_dmg() const { return false; } -#endif - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Identifier is missing."); - } - return false; - } - - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); - } - return false; - } - } - - return true; - } - -protected: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - virtual void get_export_options(List<ExportOption> *r_options) override; - -public: - virtual String get_name() const override { return "macOS"; } - virtual String get_os_name() const override { return "macOS"; } - virtual Ref<Texture2D> get_logo() const override { return logo; } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - if (use_dmg()) { - list.push_back("dmg"); - } - list.push_back("zip"); - return list; - } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("pc"); - r_features->push_back("s3tc"); - r_features->push_back("macOS"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformOSX(); - ~EditorExportPlatformOSX(); -}; - -void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - if (p_preset->get("texture_format/s3tc")) { - r_features->push_back("s3tc"); - } - if (p_preset->get("texture_format/etc")) { - r_features->push_back("etc"); - } - if (p_preset->get("texture_format/etc2")) { - r_features->push_back("etc2"); - } - - r_features->push_back("64"); -} - -void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); - -#ifdef OSX_ENABLED - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); - - if (!Engine::get_singleton()->has_singleton("GodotSharp")) { - // These entitlements are required to run managed code, and are always enabled in Mono builds. - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false)); - } - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), "")); -#endif - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); -} - -void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) { - int src_len = p_size * p_size; - - Vector<uint8_t> result; - result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario - int res_size = 0; - - uint8_t buf[128]; - int buf_size = 0; - - int i = 0; - while (i < src_len) { - uint8_t cur = p_source.ptr()[i * 4 + p_ch]; - - if (i < src_len - 2) { - if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) { - if (buf_size > 0) { - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - - uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130; - bool hit_lim = true; - - for (int j = 3; j <= lim; j++) { - if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) { - hit_lim = false; - i = i + j - 1; - result.write[res_size++] = (uint8_t)(j - 3 + 0x80); - result.write[res_size++] = cur; - break; - } - } - if (hit_lim) { - result.write[res_size++] = (uint8_t)(lim - 3 + 0x80); - result.write[res_size++] = cur; - i = i + lim; - } - } else { - buf[buf_size++] = cur; - if (buf_size == 128) { - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - } - } else { - buf[buf_size++] = cur; - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - - i++; - } - - int ofs = p_dest.size(); - p_dest.resize(p_dest.size() + res_size); - memcpy(&p_dest.write[ofs], result.ptr(), res_size); -} - -void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { - Ref<ImageTexture> it = memnew(ImageTexture); - - Vector<uint8_t> data; - - data.resize(8); - data.write[0] = 'i'; - data.write[1] = 'c'; - data.write[2] = 'n'; - data.write[3] = 's'; - - struct MacOSIconInfo { - const char *name; - const char *mask_name; - bool is_png; - int size; - }; - - static const MacOSIconInfo icon_infos[] = { - { "ic10", "", true, 1024 }, //1024x1024 32-bit PNG and 512x512@2x 32-bit "retina" PNG - { "ic09", "", true, 512 }, //512×512 32-bit PNG - { "ic14", "", true, 512 }, //256x256@2x 32-bit "retina" PNG - { "ic08", "", true, 256 }, //256×256 32-bit PNG - { "ic13", "", true, 256 }, //128x128@2x 32-bit "retina" PNG - { "ic07", "", true, 128 }, //128x128 32-bit PNG - { "ic12", "", true, 64 }, //32x32@2x 32-bit "retina" PNG - { "ic11", "", true, 32 }, //16x16@2x 32-bit "retina" PNG - { "il32", "l8mk", false, 32 }, //32x32 24-bit RLE + 8-bit uncompressed mask - { "is32", "s8mk", false, 16 } //16x16 24-bit RLE + 8-bit uncompressed mask - }; - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy? - copy->convert(Image::FORMAT_RGBA8); - copy->resize(icon_infos[i].size, icon_infos[i].size); - - if (icon_infos[i].is_png) { - // Encode PNG icon. - it->create_from_image(copy); - String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png"); - ResourceSaver::save(path, it); - - FileAccess *f = FileAccess::open(path, FileAccess::READ); - if (!f) { - // Clean up generated file. - DirAccess::remove_file_or_error(path); - ERR_FAIL(); - } - - int ofs = data.size(); - uint64_t len = f->get_length(); - data.resize(data.size() + len + 8); - f->get_buffer(&data.write[ofs + 8], len); - memdelete(f); - len += 8; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].name, 4); - encode_uint32(len, &data.write[ofs + 4]); - - // Clean up generated file. - DirAccess::remove_file_or_error(path); - - } else { - Vector<uint8_t> src_data = copy->get_data(); - - //encode 24bit RGB RLE icon - { - int ofs = data.size(); - data.resize(data.size() + 8); - - _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R - _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G - _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B - - int len = data.size() - ofs; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - - //encode 8bit mask uncompressed icon - { - int ofs = data.size(); - int len = copy->get_width() * copy->get_height(); - data.resize(data.size() + len + 8); - - for (int j = 0; j < len; j++) { - data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3]; - } - len += 8; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].mask_name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - } - } - - uint32_t total_len = data.size(); - total_len = BSWAP32(total_len); - encode_uint32(total_len, &data.write[4]); - - p_data = data; -} - -void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) { - String str; - String strnew; - str.parse_utf8((const char *)plist.ptr(), plist.size()); - Vector<String> lines = str.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$binary") != -1) { - strnew += lines[i].replace("$binary", p_binary) + "\n"; - } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", p_binary) + "\n"; - } else if (lines[i].find("$info") != -1) { - strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; - } else if (lines[i].find("$bundle_identifier") != -1) { - strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; - } else if (lines[i].find("$short_version") != -1) { - strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; - } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; - } else if (lines[i].find("$signature") != -1) { - strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; - } else if (lines[i].find("$app_category") != -1) { - String cat = p_preset->get("application/app_category"); - strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n"; - } else if (lines[i].find("$copyright") != -1) { - strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; - } else if (lines[i].find("$highres") != -1) { - strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n"; - } else if (lines[i].find("$camera_usage_description") != -1) { - String description = p_preset->get("privacy/camera_usage_description"); - strnew += lines[i].replace("$camera_usage_description", description) + "\n"; - } else if (lines[i].find("$microphone_usage_description") != -1) { - String description = p_preset->get("privacy/microphone_usage_description"); - strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; - } else { - strnew += lines[i] + "\n"; - } - } - - CharString cs = strnew.utf8(); - plist.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - plist.write[i] = cs[i]; - } -} - -/** - If we're running the OSX version of the Godot editor we'll: - - export our application bundle to a temporary folder - - attempt to code sign it - - and then wrap it up in a DMG -**/ - -Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) { -#ifdef OSX_ENABLED - List<String> args; - - args.push_back("altool"); - args.push_back("--notarize-app"); - - args.push_back("--primary-bundle-id"); - args.push_back(p_preset->get("application/bundle_identifier")); - - args.push_back("--username"); - args.push_back(p_preset->get("notarization/apple_id_name")); - - args.push_back("--password"); - args.push_back(p_preset->get("notarization/apple_id_password")); - - args.push_back("--type"); - args.push_back("osx"); - - if (p_preset->get("notarization/apple_team_id")) { - args.push_back("--asc-provider"); - args.push_back(p_preset->get("notarization/apple_team_id")); - } - - args.push_back("--file"); - args.push_back(p_path); - - String str; - Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("altool (" + p_path + "):\n" + str); - if (str.find("RequestUUID") == -1) { - EditorNode::add_io_error("altool: " + str); - return FAILED; - } else { - print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."); - print_line(" You can check progress manually by opening a Terminal and running the following command:"); - print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\""); - } - -#endif - - return OK; -} - -Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) { -#ifdef OSX_ENABLED - List<String> args; - - if (p_preset->get("codesign/timestamp")) { - args.push_back("--timestamp"); - } - if (p_preset->get("codesign/hardened_runtime")) { - args.push_back("--options"); - args.push_back("runtime"); - } - - if (p_path.get_extension() != "dmg") { - args.push_back("--entitlements"); - args.push_back(p_ent_path); - } - - PackedStringArray user_args = p_preset->get("codesign/custom_options"); - for (int i = 0; i < user_args.size(); i++) { - String user_arg = user_args[i].strip_edges(); - if (!user_arg.is_empty()) { - args.push_back(user_arg); - } - } - - args.push_back("-s"); - if (p_preset->get("codesign/identity") == "") { - args.push_back("-"); - } else { - args.push_back(p_preset->get("codesign/identity")); - } - - args.push_back("-v"); /* provide some more feedback */ - - if (p_preset->get("codesign/replace_existing_signature")) { - args.push_back("-f"); - } - - args.push_back(p_path); - - String str; - Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("codesign (" + p_path + "):\n" + str); - if (str.find("no identity found") != -1) { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } - if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) { - EditorNode::add_io_error("codesign: invalid entitlements file"); - return FAILED; - } -#endif - - return OK; -} - -Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { - List<String> args; - - if (FileAccess::exists(p_dmg_path)) { - OS::get_singleton()->move_to_trash(p_dmg_path); - } - - args.push_back("create"); - args.push_back(p_dmg_path); - args.push_back("-volname"); - args.push_back(p_pkg_name); - args.push_back("-fs"); - args.push_back("HFS+"); - args.push_back("-srcfolder"); - args.push_back(p_app_path_name); - - String str; - Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("hdiutil returned: " + str); - if (str.find("create failed") != -1) { - if (str.find("File exists") != -1) { - EditorNode::add_io_error("hdiutil: create failed - file exists"); - } else { - EditorNode::add_io_error("hdiutil: create failed"); - } - return FAILED; - } - - return OK; -} - -Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_pkg_name; - - EditorProgress ep("export", "Exporting for OSX", 3, true); - - if (p_debug) { - src_pkg_name = p_preset->get("custom_template/debug"); - } else { - src_pkg_name = p_preset->get("custom_template/release"); - } - - if (src_pkg_name == "") { - String err; - src_pkg_name = find_export_template("osx.zip", &err); - if (src_pkg_name == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - if (ep.step("Creating app", 0)) { - return ERR_SKIP; - } - - unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); - if (!src_pkg_zip) { - EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(src_pkg_zip); - - String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; - - String pkg_name; - if (p_preset->get("application/name") != "") { - pkg_name = p_preset->get("application/name"); // app_name - } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - pkg_name = "Unnamed"; - } - - pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name); - - String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip"; - - // Create our application bundle. - String tmp_app_dir_name = pkg_name + ".app"; - String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name); - print_line("Exporting to " + tmp_app_path_name); - - Error err = OK; - - DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name); - if (!tmp_app_dir) { - err = ERR_CANT_CREATE; - } - - // Create our folder structure. - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); - } - - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); - } - - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); - } - - // Now process our template. - bool found_binary = false; - int total_size = 0; - Vector<String> dylibs_found; - - while (ret == UNZ_OK && err == OK) { - bool is_execute = false; - - // Get filename. - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); - - String file = fname; - - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - // Read. - unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); - unzCloseCurrentFile(src_pkg_zip); - - // Write. - file = file.replace_first("osx_template.app/", ""); - - if (file == "Contents/Info.plist") { - _fix_plist(p_preset, data, pkg_name); - } - - if (file.begins_with("Contents/MacOS/godot_")) { - if (file != "Contents/MacOS/" + binary_to_use) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - found_binary = true; - is_execute = true; - file = "Contents/MacOS/" + pkg_name; - } - - if (file == "Contents/Resources/icon.icns") { - // See if there is an icon. - String iconpath; - if (p_preset->get("application/icon") != "") { - iconpath = p_preset->get("application/icon"); - } else { - iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); - } - - if (iconpath != "") { - if (iconpath.get_extension() == "icns") { - FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ); - if (icon) { - data.resize(icon->get_length()); - icon->get_buffer(&data.write[0], icon->get_length()); - icon->close(); - memdelete(icon); - } - } else { - Ref<Image> icon; - icon.instantiate(); - icon->load(iconpath); - if (!icon->is_empty()) { - _make_icon(icon, data); - } - } - } - } - - if (data.size() > 0) { - if (file.find("/data.mono.osx.64.release_debug/") != -1) { - if (!p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/"); - } - if (file.find("/data.mono.osx.64.release/") != -1) { - if (p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/"); - } - - if (file.ends_with(".dylib")) { - dylibs_found.push_back(file); - } - - print_line("ADDING: " + file + " size: " + itos(data.size())); - total_size += data.size(); - - // Write it into our application bundle. - file = tmp_app_path_name.plus_file(file); - if (err == OK) { - err = tmp_app_dir->make_dir_recursive(file.get_base_dir()); - } - if (err == OK) { - FileAccess *f = FileAccess::open(file, FileAccess::WRITE); - if (f) { - f->store_buffer(data.ptr(), data.size()); - f->close(); - if (is_execute) { - // chmod with 0755 if the file is executable. - FileAccess::set_unix_permissions(file, 0755); - } - memdelete(f); - } else { - err = ERR_CANT_CREATE; - } - } - } - - ret = unzGoToNextFile(src_pkg_zip); - } - - // We're done with our source zip. - unzClose(src_pkg_zip); - - if (!found_binary) { - ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); - err = ERR_FILE_NOT_FOUND; - } - - if (err == OK) { - if (ep.step("Making PKG", 1)) { - return ERR_SKIP; - } - - String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; - Vector<SharedObject> shared_objects; - err = save_pack(p_preset, pack_path, &shared_objects); - - // See if we can code sign our new package. - bool sign_enabled = p_preset->get("codesign/enable"); - - String ent_path = p_preset->get("codesign/entitlements/custom_file"); - if (sign_enabled && (ent_path == "")) { - ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements"); - - FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE); - if (ent_f) { - ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); - ent_f->store_line("<plist version=\"1.0\">"); - ent_f->store_line("<dict>"); - if (Engine::get_singleton()->has_singleton("GodotSharp")) { - // These entitlements are required to run managed code, and are always enabled in Mono builds. - ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); - ent_f->store_line("<true/>"); - } else { - if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) { - ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) { - ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) { - ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); - ent_f->store_line("<true/>"); - } - } - - if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { - ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/audio_input")) { - ent_f->store_line("<key>com.apple.security.device.audio-input</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/camera")) { - ent_f->store_line("<key>com.apple.security.device.camera</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/location")) { - ent_f->store_line("<key>com.apple.security.personal-information.location</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/address_book")) { - ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/calendars")) { - ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/photos_library")) { - ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/apple_events")) { - ent_f->store_line("<key>com.apple.security.automation.apple-events</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/debugging")) { - ent_f->store_line("<key>com.apple.security.get-task-allow</key>"); - ent_f->store_line("<true/>"); - } - - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) { - ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); - ent_f->store_line("<true/>"); - - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) { - ent_f->store_line("<key>com.apple.security.network.server</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) { - ent_f->store_line("<key>com.apple.security.network.client</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) { - ent_f->store_line("<key>com.apple.security.device.usb</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) { - ent_f->store_line("<key>com.apple.security.device.bluetooth</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) { - ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) { - ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) { - ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) { - ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) { - ent_f->store_line("<key>com.apple.security.files.music.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) { - ent_f->store_line("<key>com.apple.security.files.music.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) { - ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) { - ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>"); - ent_f->store_line("<true/>"); - } - } - - ent_f->store_line("</dict>"); - ent_f->store_line("</plist>"); - - ent_f->close(); - memdelete(ent_f); - } else { - err = ERR_CANT_CREATE; - } - } - - if (err == OK) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < shared_objects.size(); i++) { - String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); - if (da->dir_exists(src_path)) { -#ifndef UNIX_ENABLED - WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!"); -#endif - print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - if (err == OK) { - err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true); - } - } else { - print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - } - if (err == OK && sign_enabled) { - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path); - } - } - memdelete(da); - } - - if (sign_enabled) { - for (int i = 0; i < dylibs_found.size(); i++) { - if (err == OK) { - err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path); - } - } - } - - if (err == OK && sign_enabled) { - if (ep.step("Code signing bundle", 2)) { - return ERR_SKIP; - } - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path); - } - - if (export_format == "dmg") { - // Create a DMG. - if (err == OK) { - if (ep.step("Making DMG", 3)) { - return ERR_SKIP; - } - err = _create_dmg(p_path, pkg_name, tmp_app_path_name); - } - // Sign DMG. - if (err == OK && sign_enabled) { - if (ep.step("Code signing DMG", 3)) { - return ERR_SKIP; - } - err = _code_sign(p_preset, p_path, ent_path); - } - } else { - // Create ZIP. - if (err == OK) { - if (ep.step("Making ZIP", 3)) { - return ERR_SKIP; - } - if (FileAccess::exists(p_path)) { - OS::get_singleton()->move_to_trash(p_path); - } - - FileAccess *dst_f = nullptr; - zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f); - zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); - - _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name); - - zipClose(zip, nullptr); - } - } - - bool noto_enabled = p_preset->get("notarization/enable"); - if (err == OK && noto_enabled) { - if (ep.step("Sending archive for notarization", 4)) { - return ERR_SKIP; - } - err = _notarize(p_preset, p_path); - } - - // Clean up temporary .app dir. - tmp_app_dir->change_dir(tmp_app_path_name); - tmp_app_dir->erase_contents_recursive(); - tmp_app_dir->change_dir(".."); - tmp_app_dir->remove(tmp_app_dir_name); - } - - return err; -} - -void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { - String dir = p_root_path.plus_file(p_folder); - - DirAccess *da = DirAccess::open(dir); - da->list_dir_begin(); - String f; - while ((f = da->get_next()) != "") { - if (f == "." || f == "..") { - continue; - } - if (da->is_link(f)) { - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - // 0120000: symbolic link type - // 0000755: permissions rwxr-xr-x - // 0000644: permissions rw-r--r-- - uint32_t _mode = 0120644; - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); - zipfi.internal_fa = 0; - - zipOpenNewFileInZip4(p_zip, - p_folder.plus_file(f).utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION, - 0, - -MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, - nullptr, - 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); - - String target = da->read_link(f); - zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size()); - zipCloseFileInZip(p_zip); - } else if (da->current_is_dir()) { - _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); - } else { - bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)); - - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - // 0100000: regular file type - // 0000755: permissions rwxr-xr-x - // 0000644: permissions rw-r--r-- - uint32_t _mode = (is_executable ? 0100755 : 0100644); - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); - zipfi.internal_fa = 0; - - zipOpenNewFileInZip4(p_zip, - p_folder.plus_file(f).utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION, - 0, - -MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, - nullptr, - 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); - - Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f)); - zipWriteInFileInZip(p_zip, array.ptr(), array.size()); - zipCloseFileInZip(p_zip); - } - } - da->list_dir_end(); - memdelete(da); -} - -bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - bool dvalid = exists_export_template("osx.zip", &err); - bool rvalid = dvalid; // Both in the same ZIP. - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - String identifier = p_preset->get("application/bundle_identifier"); - String pn_err; - if (!is_package_name_valid(identifier, &pn_err)) { - err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n"; - valid = false; - } - - bool sign_enabled = p_preset->get("codesign/enable"); - bool noto_enabled = p_preset->get("notarization/enable"); - if (noto_enabled) { - if (!sign_enabled) { - err += TTR("Notarization: code signing required.") + "\n"; - valid = false; - } - bool hr_enabled = p_preset->get("codesign/hardened_runtime"); - if (!hr_enabled) { - err += TTR("Notarization: hardened runtime required.") + "\n"; - valid = false; - } - if (p_preset->get("notarization/apple_id_name") == "") { - err += TTR("Notarization: Apple ID name not specified.") + "\n"; - valid = false; - } - if (p_preset->get("notarization/apple_id_password") == "") { - err += TTR("Notarization: Apple ID password not specified.") + "\n"; - valid = false; - } - } - - if (!err.is_empty()) { - r_error = err; - } - return valid; -} - -EditorExportPlatformOSX::EditorExportPlatformOSX() { - Ref<Image> img = memnew(Image(_osx_logo)); - logo.instantiate(); - logo->create_from_image(img); -} - -EditorExportPlatformOSX::~EditorExportPlatformOSX() { -} +#include "export_plugin.h" void register_osx_exporter() { Ref<EditorExportPlatformOSX> platform; diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp new file mode 100644 index 0000000000..53ae1ea6fe --- /dev/null +++ b/platform/osx/export/export_plugin.cpp @@ -0,0 +1,1085 @@ +/*************************************************************************/ +/* export_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 "export_plugin.h" + +void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + if (p_preset->get("texture_format/s3tc")) { + r_features->push_back("s3tc"); + } + if (p_preset->get("texture_format/etc")) { + r_features->push_back("etc"); + } + if (p_preset->get("texture_format/etc2")) { + r_features->push_back("etc2"); + } + + r_features->push_back("64"); +} + +void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); + +#ifdef OSX_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); + + if (!Engine::get_singleton()->has_singleton("GodotSharp")) { + // These entitlements are required to run managed code, and are always enabled in Mono builds. + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), "")); +#endif + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); +} + +void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) { + int src_len = p_size * p_size; + + Vector<uint8_t> result; + result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario + int res_size = 0; + + uint8_t buf[128]; + int buf_size = 0; + + int i = 0; + while (i < src_len) { + uint8_t cur = p_source.ptr()[i * 4 + p_ch]; + + if (i < src_len - 2) { + if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) { + if (buf_size > 0) { + result.write[res_size++] = (uint8_t)(buf_size - 1); + memcpy(&result.write[res_size], &buf, buf_size); + res_size += buf_size; + buf_size = 0; + } + + uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130; + bool hit_lim = true; + + for (int j = 3; j <= lim; j++) { + if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) { + hit_lim = false; + i = i + j - 1; + result.write[res_size++] = (uint8_t)(j - 3 + 0x80); + result.write[res_size++] = cur; + break; + } + } + if (hit_lim) { + result.write[res_size++] = (uint8_t)(lim - 3 + 0x80); + result.write[res_size++] = cur; + i = i + lim; + } + } else { + buf[buf_size++] = cur; + if (buf_size == 128) { + result.write[res_size++] = (uint8_t)(buf_size - 1); + memcpy(&result.write[res_size], &buf, buf_size); + res_size += buf_size; + buf_size = 0; + } + } + } else { + buf[buf_size++] = cur; + result.write[res_size++] = (uint8_t)(buf_size - 1); + memcpy(&result.write[res_size], &buf, buf_size); + res_size += buf_size; + buf_size = 0; + } + + i++; + } + + int ofs = p_dest.size(); + p_dest.resize(p_dest.size() + res_size); + memcpy(&p_dest.write[ofs], result.ptr(), res_size); +} + +void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { + Ref<ImageTexture> it = memnew(ImageTexture); + + Vector<uint8_t> data; + + data.resize(8); + data.write[0] = 'i'; + data.write[1] = 'c'; + data.write[2] = 'n'; + data.write[3] = 's'; + + struct MacOSIconInfo { + const char *name; + const char *mask_name; + bool is_png; + int size; + }; + + static const MacOSIconInfo icon_infos[] = { + { "ic10", "", true, 1024 }, //1024x1024 32-bit PNG and 512x512@2x 32-bit "retina" PNG + { "ic09", "", true, 512 }, //512×512 32-bit PNG + { "ic14", "", true, 512 }, //256x256@2x 32-bit "retina" PNG + { "ic08", "", true, 256 }, //256×256 32-bit PNG + { "ic13", "", true, 256 }, //128x128@2x 32-bit "retina" PNG + { "ic07", "", true, 128 }, //128x128 32-bit PNG + { "ic12", "", true, 64 }, //32x32@2x 32-bit "retina" PNG + { "ic11", "", true, 32 }, //16x16@2x 32-bit "retina" PNG + { "il32", "l8mk", false, 32 }, //32x32 24-bit RLE + 8-bit uncompressed mask + { "is32", "s8mk", false, 16 } //16x16 24-bit RLE + 8-bit uncompressed mask + }; + + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy? + copy->convert(Image::FORMAT_RGBA8); + copy->resize(icon_infos[i].size, icon_infos[i].size); + + if (icon_infos[i].is_png) { + // Encode PNG icon. + it->create_from_image(copy); + String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png"); + ResourceSaver::save(path, it); + + FileAccess *f = FileAccess::open(path, FileAccess::READ); + if (!f) { + // Clean up generated file. + DirAccess::remove_file_or_error(path); + ERR_FAIL(); + } + + int ofs = data.size(); + uint64_t len = f->get_length(); + data.resize(data.size() + len + 8); + f->get_buffer(&data.write[ofs + 8], len); + memdelete(f); + len += 8; + len = BSWAP32(len); + memcpy(&data.write[ofs], icon_infos[i].name, 4); + encode_uint32(len, &data.write[ofs + 4]); + + // Clean up generated file. + DirAccess::remove_file_or_error(path); + + } else { + Vector<uint8_t> src_data = copy->get_data(); + + //encode 24bit RGB RLE icon + { + int ofs = data.size(); + data.resize(data.size() + 8); + + _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R + _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G + _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B + + int len = data.size() - ofs; + len = BSWAP32(len); + memcpy(&data.write[ofs], icon_infos[i].name, 4); + encode_uint32(len, &data.write[ofs + 4]); + } + + //encode 8bit mask uncompressed icon + { + int ofs = data.size(); + int len = copy->get_width() * copy->get_height(); + data.resize(data.size() + len + 8); + + for (int j = 0; j < len; j++) { + data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3]; + } + len += 8; + len = BSWAP32(len); + memcpy(&data.write[ofs], icon_infos[i].mask_name, 4); + encode_uint32(len, &data.write[ofs + 4]); + } + } + } + + uint32_t total_len = data.size(); + total_len = BSWAP32(total_len); + encode_uint32(total_len, &data.write[4]); + + p_data = data; +} + +void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) { + String str; + String strnew; + str.parse_utf8((const char *)plist.ptr(), plist.size()); + Vector<String> lines = str.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$binary") != -1) { + strnew += lines[i].replace("$binary", p_binary) + "\n"; + } else if (lines[i].find("$name") != -1) { + strnew += lines[i].replace("$name", p_binary) + "\n"; + } else if (lines[i].find("$info") != -1) { + strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; + } else if (lines[i].find("$bundle_identifier") != -1) { + strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; + } else if (lines[i].find("$short_version") != -1) { + strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; + } else if (lines[i].find("$version") != -1) { + strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + } else if (lines[i].find("$signature") != -1) { + strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; + } else if (lines[i].find("$app_category") != -1) { + String cat = p_preset->get("application/app_category"); + strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n"; + } else if (lines[i].find("$copyright") != -1) { + strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; + } else if (lines[i].find("$highres") != -1) { + strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n"; + } else if (lines[i].find("$camera_usage_description") != -1) { + String description = p_preset->get("privacy/camera_usage_description"); + strnew += lines[i].replace("$camera_usage_description", description) + "\n"; + } else if (lines[i].find("$microphone_usage_description") != -1) { + String description = p_preset->get("privacy/microphone_usage_description"); + strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; + } else { + strnew += lines[i] + "\n"; + } + } + + CharString cs = strnew.utf8(); + plist.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + plist.write[i] = cs[i]; + } +} + +/** + If we're running the OSX version of the Godot editor we'll: + - export our application bundle to a temporary folder + - attempt to code sign it + - and then wrap it up in a DMG +**/ + +Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) { +#ifdef OSX_ENABLED + List<String> args; + + args.push_back("altool"); + args.push_back("--notarize-app"); + + args.push_back("--primary-bundle-id"); + args.push_back(p_preset->get("application/bundle_identifier")); + + args.push_back("--username"); + args.push_back(p_preset->get("notarization/apple_id_name")); + + args.push_back("--password"); + args.push_back(p_preset->get("notarization/apple_id_password")); + + args.push_back("--type"); + args.push_back("osx"); + + if (p_preset->get("notarization/apple_team_id")) { + args.push_back("--asc-provider"); + args.push_back(p_preset->get("notarization/apple_team_id")); + } + + args.push_back("--file"); + args.push_back(p_path); + + String str; + Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("altool (" + p_path + "):\n" + str); + if (str.find("RequestUUID") == -1) { + EditorNode::add_io_error("altool: " + str); + return FAILED; + } else { + print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."); + print_line(" You can check progress manually by opening a Terminal and running the following command:"); + print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\""); + } + +#endif + + return OK; +} + +Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) { +#ifdef OSX_ENABLED + List<String> args; + + if (p_preset->get("codesign/timestamp")) { + args.push_back("--timestamp"); + } + if (p_preset->get("codesign/hardened_runtime")) { + args.push_back("--options"); + args.push_back("runtime"); + } + + if (p_path.get_extension() != "dmg") { + args.push_back("--entitlements"); + args.push_back(p_ent_path); + } + + PackedStringArray user_args = p_preset->get("codesign/custom_options"); + for (int i = 0; i < user_args.size(); i++) { + String user_arg = user_args[i].strip_edges(); + if (!user_arg.is_empty()) { + args.push_back(user_arg); + } + } + + args.push_back("-s"); + if (p_preset->get("codesign/identity") == "") { + args.push_back("-"); + } else { + args.push_back(p_preset->get("codesign/identity")); + } + + args.push_back("-v"); /* provide some more feedback */ + + if (p_preset->get("codesign/replace_existing_signature")) { + args.push_back("-f"); + } + + args.push_back(p_path); + + String str; + Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("codesign (" + p_path + "):\n" + str); + if (str.find("no identity found") != -1) { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) { + EditorNode::add_io_error("codesign: invalid entitlements file"); + return FAILED; + } +#endif + + return OK; +} + +Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { + List<String> args; + + if (FileAccess::exists(p_dmg_path)) { + OS::get_singleton()->move_to_trash(p_dmg_path); + } + + args.push_back("create"); + args.push_back(p_dmg_path); + args.push_back("-volname"); + args.push_back(p_pkg_name); + args.push_back("-fs"); + args.push_back("HFS+"); + args.push_back("-srcfolder"); + args.push_back(p_app_path_name); + + String str; + Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("hdiutil returned: " + str); + if (str.find("create failed") != -1) { + if (str.find("File exists") != -1) { + EditorNode::add_io_error("hdiutil: create failed - file exists"); + } else { + EditorNode::add_io_error("hdiutil: create failed"); + } + return FAILED; + } + + return OK; +} + +Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_pkg_name; + + EditorProgress ep("export", "Exporting for OSX", 3, true); + + if (p_debug) { + src_pkg_name = p_preset->get("custom_template/debug"); + } else { + src_pkg_name = p_preset->get("custom_template/release"); + } + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("osx.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + if (ep.step("Creating app", 0)) { + return ERR_SKIP; + } + + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(src_pkg_zip); + + String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; + + String pkg_name; + if (p_preset->get("application/name") != "") { + pkg_name = p_preset->get("application/name"); // app_name + } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + } else { + pkg_name = "Unnamed"; + } + + pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name); + + String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip"; + + // Create our application bundle. + String tmp_app_dir_name = pkg_name + ".app"; + String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name); + print_line("Exporting to " + tmp_app_path_name); + + Error err = OK; + + DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name); + if (!tmp_app_dir) { + err = ERR_CANT_CREATE; + } + + // Create our folder structure. + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); + } + + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); + } + + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); + } + + // Now process our template. + bool found_binary = false; + int total_size = 0; + Vector<String> dylibs_found; + + while (ret == UNZ_OK && err == OK) { + bool is_execute = false; + + // Get filename. + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String file = fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + // Read. + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + // Write. + file = file.replace_first("osx_template.app/", ""); + + if (file == "Contents/Info.plist") { + _fix_plist(p_preset, data, pkg_name); + } + + if (file.begins_with("Contents/MacOS/godot_")) { + if (file != "Contents/MacOS/" + binary_to_use) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + found_binary = true; + is_execute = true; + file = "Contents/MacOS/" + pkg_name; + } + + if (file == "Contents/Resources/icon.icns") { + // See if there is an icon. + String iconpath; + if (p_preset->get("application/icon") != "") { + iconpath = p_preset->get("application/icon"); + } else { + iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); + } + + if (iconpath != "") { + if (iconpath.get_extension() == "icns") { + FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ); + if (icon) { + data.resize(icon->get_length()); + icon->get_buffer(&data.write[0], icon->get_length()); + icon->close(); + memdelete(icon); + } + } else { + Ref<Image> icon; + icon.instantiate(); + icon->load(iconpath); + if (!icon->is_empty()) { + _make_icon(icon, data); + } + } + } + } + + if (data.size() > 0) { + if (file.find("/data.mono.osx.64.release_debug/") != -1) { + if (!p_debug) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/"); + } + if (file.find("/data.mono.osx.64.release/") != -1) { + if (p_debug) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/"); + } + + if (file.ends_with(".dylib")) { + dylibs_found.push_back(file); + } + + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + // Write it into our application bundle. + file = tmp_app_path_name.plus_file(file); + if (err == OK) { + err = tmp_app_dir->make_dir_recursive(file.get_base_dir()); + } + if (err == OK) { + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (f) { + f->store_buffer(data.ptr(), data.size()); + f->close(); + if (is_execute) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(file, 0755); + } + memdelete(f); + } else { + err = ERR_CANT_CREATE; + } + } + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + // We're done with our source zip. + unzClose(src_pkg_zip); + + if (!found_binary) { + ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); + err = ERR_FILE_NOT_FOUND; + } + + if (err == OK) { + if (ep.step("Making PKG", 1)) { + return ERR_SKIP; + } + + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; + Vector<SharedObject> shared_objects; + err = save_pack(p_preset, pack_path, &shared_objects); + + // See if we can code sign our new package. + bool sign_enabled = p_preset->get("codesign/enable"); + + String ent_path = p_preset->get("codesign/entitlements/custom_file"); + if (sign_enabled && (ent_path == "")) { + ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements"); + + FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE); + if (ent_f) { + ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); + ent_f->store_line("<plist version=\"1.0\">"); + ent_f->store_line("<dict>"); + if (Engine::get_singleton()->has_singleton("GodotSharp")) { + // These entitlements are required to run managed code, and are always enabled in Mono builds. + ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); + ent_f->store_line("<true/>"); + ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); + ent_f->store_line("<true/>"); + ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); + ent_f->store_line("<true/>"); + } else { + if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) { + ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) { + ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) { + ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); + ent_f->store_line("<true/>"); + } + } + + if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { + ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/audio_input")) { + ent_f->store_line("<key>com.apple.security.device.audio-input</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/camera")) { + ent_f->store_line("<key>com.apple.security.device.camera</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/location")) { + ent_f->store_line("<key>com.apple.security.personal-information.location</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/address_book")) { + ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/calendars")) { + ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/photos_library")) { + ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/apple_events")) { + ent_f->store_line("<key>com.apple.security.automation.apple-events</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/debugging")) { + ent_f->store_line("<key>com.apple.security.get-task-allow</key>"); + ent_f->store_line("<true/>"); + } + + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) { + ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); + ent_f->store_line("<true/>"); + + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) { + ent_f->store_line("<key>com.apple.security.network.server</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) { + ent_f->store_line("<key>com.apple.security.network.client</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) { + ent_f->store_line("<key>com.apple.security.device.usb</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) { + ent_f->store_line("<key>com.apple.security.device.bluetooth</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) { + ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) { + ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) { + ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) { + ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) { + ent_f->store_line("<key>com.apple.security.files.music.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) { + ent_f->store_line("<key>com.apple.security.files.music.read-write</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) { + ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) { + ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>"); + ent_f->store_line("<true/>"); + } + } + + ent_f->store_line("</dict>"); + ent_f->store_line("</plist>"); + + ent_f->close(); + memdelete(ent_f); + } else { + err = ERR_CANT_CREATE; + } + } + + if (err == OK) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < shared_objects.size(); i++) { + String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); + if (da->dir_exists(src_path)) { +#ifndef UNIX_ENABLED + WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!"); +#endif + print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + if (err == OK) { + err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true); + } + } else { + print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + } + if (err == OK && sign_enabled) { + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path); + } + } + memdelete(da); + } + + if (sign_enabled) { + for (int i = 0; i < dylibs_found.size(); i++) { + if (err == OK) { + err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path); + } + } + } + + if (err == OK && sign_enabled) { + if (ep.step("Code signing bundle", 2)) { + return ERR_SKIP; + } + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path); + } + + if (export_format == "dmg") { + // Create a DMG. + if (err == OK) { + if (ep.step("Making DMG", 3)) { + return ERR_SKIP; + } + err = _create_dmg(p_path, pkg_name, tmp_app_path_name); + } + // Sign DMG. + if (err == OK && sign_enabled) { + if (ep.step("Code signing DMG", 3)) { + return ERR_SKIP; + } + err = _code_sign(p_preset, p_path, ent_path); + } + } else { + // Create ZIP. + if (err == OK) { + if (ep.step("Making ZIP", 3)) { + return ERR_SKIP; + } + if (FileAccess::exists(p_path)) { + OS::get_singleton()->move_to_trash(p_path); + } + + FileAccess *dst_f = nullptr; + zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f); + zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); + + _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name); + + zipClose(zip, nullptr); + } + } + + bool noto_enabled = p_preset->get("notarization/enable"); + if (err == OK && noto_enabled) { + if (ep.step("Sending archive for notarization", 4)) { + return ERR_SKIP; + } + err = _notarize(p_preset, p_path); + } + + // Clean up temporary .app dir. + tmp_app_dir->change_dir(tmp_app_path_name); + tmp_app_dir->erase_contents_recursive(); + tmp_app_dir->change_dir(".."); + tmp_app_dir->remove(tmp_app_dir_name); + } + + return err; +} + +void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { + String dir = p_root_path.plus_file(p_folder); + + DirAccess *da = DirAccess::open(dir); + da->list_dir_begin(); + String f; + while ((f = da->get_next()) != "") { + if (f == "." || f == "..") { + continue; + } + if (da->is_link(f)) { + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + // 0120000: symbolic link type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = 0120644; + zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.internal_fa = 0; + + zipOpenNewFileInZip4(p_zip, + p_folder.plus_file(f).utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions + 0); + + String target = da->read_link(f); + zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size()); + zipCloseFileInZip(p_zip); + } else if (da->current_is_dir()) { + _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); + } else { + bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)); + + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + // 0100000: regular file type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = (is_executable ? 0100755 : 0100644); + zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.internal_fa = 0; + + zipOpenNewFileInZip4(p_zip, + p_folder.plus_file(f).utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions + 0); + + Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f)); + zipWriteInFileInZip(p_zip, array.ptr(), array.size()); + zipCloseFileInZip(p_zip); + } + } + da->list_dir_end(); + memdelete(da); +} + +bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + bool dvalid = exists_export_template("osx.zip", &err); + bool rvalid = dvalid; // Both in the same ZIP. + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + String identifier = p_preset->get("application/bundle_identifier"); + String pn_err; + if (!is_package_name_valid(identifier, &pn_err)) { + err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n"; + valid = false; + } + + bool sign_enabled = p_preset->get("codesign/enable"); + bool noto_enabled = p_preset->get("notarization/enable"); + if (noto_enabled) { + if (!sign_enabled) { + err += TTR("Notarization: code signing required.") + "\n"; + valid = false; + } + bool hr_enabled = p_preset->get("codesign/hardened_runtime"); + if (!hr_enabled) { + err += TTR("Notarization: hardened runtime required.") + "\n"; + valid = false; + } + if (p_preset->get("notarization/apple_id_name") == "") { + err += TTR("Notarization: Apple ID name not specified.") + "\n"; + valid = false; + } + if (p_preset->get("notarization/apple_id_password") == "") { + err += TTR("Notarization: Apple ID password not specified.") + "\n"; + valid = false; + } + } + + if (!err.is_empty()) { + r_error = err; + } + return valid; +} + +EditorExportPlatformOSX::EditorExportPlatformOSX() { + Ref<Image> img = memnew(Image(_osx_logo)); + logo.instantiate(); + logo->create_from_image(img); +} + +EditorExportPlatformOSX::~EditorExportPlatformOSX() { +} diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h new file mode 100644 index 0000000000..cd85ce2aad --- /dev/null +++ b/platform/osx/export/export_plugin.h @@ -0,0 +1,128 @@ +/*************************************************************************/ +/* export_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 OSX_EXPORT_PLUGIN_H +#define OSX_EXPORT_PLUGIN_H + +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/os.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "platform/osx/logo.gen.h" + +#include <sys/stat.h> + +class EditorExportPlatformOSX : public EditorExportPlatform { + GDCLASS(EditorExportPlatformOSX, EditorExportPlatform); + + int version_code = 0; + + Ref<ImageTexture> logo; + + void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); + void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); + + Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path); + Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); + void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); + +#ifdef OSX_ENABLED + bool use_codesign() const { return true; } + bool use_dmg() const { return true; } +#else + bool use_codesign() const { return false; } + bool use_dmg() const { return false; } +#endif + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = TTR("Identifier is missing."); + } + return false; + } + + for (int i = 0; i < pname.length(); i++) { + char32_t c = pname[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { + if (r_error) { + *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); + } + return false; + } + } + + return true; + } + +protected: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + virtual void get_export_options(List<ExportOption> *r_options) override; + +public: + virtual String get_name() const override { return "macOS"; } + virtual String get_os_name() const override { return "macOS"; } + virtual Ref<Texture2D> get_logo() const override { return logo; } + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { + List<String> list; + if (use_dmg()) { + list.push_back("dmg"); + } + list.push_back("zip"); + return list; + } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual void get_platform_features(List<String> *r_features) override { + r_features->push_back("pc"); + r_features->push_back("s3tc"); + r_features->push_back("macOS"); + } + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { + } + + EditorExportPlatformOSX(); + ~EditorExportPlatformOSX(); +}; + +#endif diff --git a/platform/uwp/export/app_packager.cpp b/platform/uwp/export/app_packager.cpp new file mode 100644 index 0000000000..39a2693f75 --- /dev/null +++ b/platform/uwp/export/app_packager.cpp @@ -0,0 +1,474 @@ +/*************************************************************************/ +/* app_packager.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 "app_packager.h" + +String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) { + unsigned char hash[32]; + char base64[45]; + + CryptoCore::sha256(p_block_data, p_block_len, hash); + size_t len = 0; + CryptoCore::b64_encode((unsigned char *)base64, 45, &len, (unsigned char *)hash, 32); + base64[44] = '\0'; + + return String(base64); +} + +void AppxPackager::make_block_map(const String &p_path) { + FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); + + tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); + tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">"); + + for (int i = 0; i < file_metadata.size(); i++) { + FileMeta file = file_metadata[i]; + + tmp_file->store_string( + "<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); + + for (int j = 0; j < file.hashes.size(); j++) { + tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" "); + if (file.compressed) { + tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" "); + } + tmp_file->store_string("/>"); + } + + tmp_file->store_string("</File>"); + } + + tmp_file->store_string("</BlockMap>"); + + tmp_file->close(); + memdelete(tmp_file); +} + +String AppxPackager::content_type(String p_extension) { + if (p_extension == "png") { + return "image/png"; + } else if (p_extension == "jpg") { + return "image/jpg"; + } else if (p_extension == "xml") { + return "application/xml"; + } else if (p_extension == "exe" || p_extension == "dll") { + return "application/x-msdownload"; + } else { + return "application/octet-stream"; + } +} + +void AppxPackager::make_content_types(const String &p_path) { + FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); + + tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); + + Map<String, String> types; + + for (int i = 0; i < file_metadata.size(); i++) { + String ext = file_metadata[i].name.get_extension().to_lower(); + + if (types.has(ext)) { + continue; + } + + types[ext] = content_type(ext); + + tmp_file->store_string("<Default Extension=\"" + ext + "\" ContentType=\"" + types[ext] + "\" />"); + } + + // Appx signature file + tmp_file->store_string("<Default Extension=\"p7x\" ContentType=\"application/octet-stream\" />"); + + // Override for package files + tmp_file->store_string("<Override PartName=\"/AppxManifest.xml\" ContentType=\"application/vnd.ms-appx.manifest+xml\" />"); + tmp_file->store_string("<Override PartName=\"/AppxBlockMap.xml\" ContentType=\"application/vnd.ms-appx.blockmap+xml\" />"); + tmp_file->store_string("<Override PartName=\"/AppxSignature.p7x\" ContentType=\"application/vnd.ms-appx.signature\" />"); + tmp_file->store_string("<Override PartName=\"/AppxMetadata/CodeIntegrity.cat\" ContentType=\"application/vnd.ms-pkiseccat\" />"); + + tmp_file->store_string("</Types>"); + + tmp_file->close(); + memdelete(tmp_file); +} + +Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { + Vector<uint8_t> buf; + buf.resize(BASE_FILE_HEADER_SIZE + p_file_meta.name.length()); + + int offs = 0; + // Write magic + offs += buf_put_int32(FILE_HEADER_MAGIC, &buf.write[offs]); + + // Version + offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); + + // Special flag + offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); + + // Compression + offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf.write[offs]); + + // File date and time + offs += buf_put_int32(0, &buf.write[offs]); + + // CRC-32 + offs += buf_put_int32(p_file_meta.file_crc32, &buf.write[offs]); + + // Compressed size + offs += buf_put_int32(p_file_meta.compressed_size, &buf.write[offs]); + + // Uncompressed size + offs += buf_put_int32(p_file_meta.uncompressed_size, &buf.write[offs]); + + // File name length + offs += buf_put_int16(p_file_meta.name.length(), &buf.write[offs]); + + // Extra data length + offs += buf_put_int16(0, &buf.write[offs]); + + // File name + offs += buf_put_string(p_file_meta.name, &buf.write[offs]); + + // Done! + return buf; +} + +void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_hash) { + Vector<uint8_t> &buf = central_dir_data; + int offs = buf.size(); + buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); + + // Write magic + offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf.write[offs]); + + // ZIP versions + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); + + // General purpose flag + offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); + + // Compression + offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf.write[offs]); + + // Modification date/time + offs += buf_put_int32(0, &buf.write[offs]); + + // Crc-32 + offs += buf_put_int32(p_file.file_crc32, &buf.write[offs]); + + // File sizes + offs += buf_put_int32(p_file.compressed_size, &buf.write[offs]); + offs += buf_put_int32(p_file.uncompressed_size, &buf.write[offs]); + + // File name length + offs += buf_put_int16(p_file.name.length(), &buf.write[offs]); + + // Extra field length + offs += buf_put_int16(0, &buf.write[offs]); + + // Comment length + offs += buf_put_int16(0, &buf.write[offs]); + + // Disk number start, internal/external file attributes + for (int i = 0; i < 8; i++) { + buf.write[offs++] = 0; + } + + // Relative offset + offs += buf_put_int32(p_file.zip_offset, &buf.write[offs]); + + // File name + offs += buf_put_string(p_file.name, &buf.write[offs]); + + // Done! +} + +Vector<uint8_t> AppxPackager::make_end_of_central_record() { + Vector<uint8_t> buf; + buf.resize(ZIP64_END_OF_CENTRAL_DIR_SIZE + 12 + END_OF_CENTRAL_DIR_SIZE); // Size plus magic + + int offs = 0; + + // Write magic + offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); + + // Size of this record + offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf.write[offs]); + + // Version (yes, twice) + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + + // Disk number + for (int i = 0; i < 8; i++) { + buf.write[offs++] = 0; + } + + // Number of entries (total and per disk) + offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); + offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); + + // Size of central dir + offs += buf_put_int64(central_dir_data.size(), &buf.write[offs]); + + // Central dir offset + offs += buf_put_int64(central_dir_offset, &buf.write[offs]); + + ////// ZIP64 locator + + // Write magic for zip64 central dir locator + offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf.write[offs]); + + // Disk number + for (int i = 0; i < 4; i++) { + buf.write[offs++] = 0; + } + + // Relative offset + offs += buf_put_int64(end_of_central_dir_offset, &buf.write[offs]); + + // Number of disks + offs += buf_put_int32(1, &buf.write[offs]); + + /////// End of zip directory + + // Write magic for end central dir + offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); + + // Dummy stuff for Zip64 + for (int i = 0; i < 4; i++) { + buf.write[offs++] = 0x0; + } + for (int i = 0; i < 12; i++) { + buf.write[offs++] = 0xFF; + } + + // Size of comments + for (int i = 0; i < 2; i++) { + buf.write[offs++] = 0; + } + + // Done! + return buf; +} + +void AppxPackager::init(FileAccess *p_fa) { + package = p_fa; + central_dir_offset = 0; + end_of_central_dir_offset = 0; +} + +Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { + if (p_file_no >= 1 && p_total_files >= 1) { + if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) { + return ERR_SKIP; + } + } + + FileMeta meta; + meta.name = p_file_name; + meta.uncompressed_size = p_len; + meta.compressed_size = p_len; + meta.compressed = p_compress; + meta.zip_offset = package->get_position(); + + Vector<uint8_t> file_buffer; + + // Data for compression + z_stream strm; + FileAccess *strm_f = nullptr; + Vector<uint8_t> strm_in; + strm_in.resize(BLOCK_SIZE); + Vector<uint8_t> strm_out; + + if (p_compress) { + strm.zalloc = zipio_alloc; + strm.zfree = zipio_free; + strm.opaque = &strm_f; + + strm_out.resize(BLOCK_SIZE + 8); + + deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); + } + + int step = 0; + + while (p_len - step > 0) { + size_t block_size = (p_len - step) > BLOCK_SIZE ? (size_t)BLOCK_SIZE : (p_len - step); + + for (uint64_t i = 0; i < block_size; i++) { + strm_in.write[i] = p_buffer[step + i]; + } + + BlockHash bh; + bh.base64_hash = hash_block(strm_in.ptr(), block_size); + + if (p_compress) { + strm.avail_in = block_size; + strm.avail_out = strm_out.size(); + strm.next_in = (uint8_t *)strm_in.ptr(); + strm.next_out = strm_out.ptrw(); + + int total_out_before = strm.total_out; + + int err = deflate(&strm, Z_FULL_FLUSH); + ERR_FAIL_COND_V(err < 0, ERR_BUG); // Negative means bug + + bh.compressed_size = strm.total_out - total_out_before; + + //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); + int start = file_buffer.size(); + file_buffer.resize(file_buffer.size() + bh.compressed_size); + for (uint64_t i = 0; i < bh.compressed_size; i++) { + file_buffer.write[start + i] = strm_out[i]; + } + } else { + bh.compressed_size = block_size; + //package->store_buffer(strm_in.ptr(), block_size); + int start = file_buffer.size(); + file_buffer.resize(file_buffer.size() + block_size); + for (uint64_t i = 0; i < bh.compressed_size; i++) { + file_buffer.write[start + i] = strm_in[i]; + } + } + + meta.hashes.push_back(bh); + + step += block_size; + } + + if (p_compress) { + strm.avail_in = 0; + strm.avail_out = strm_out.size(); + strm.next_in = (uint8_t *)strm_in.ptr(); + strm.next_out = strm_out.ptrw(); + + int total_out_before = strm.total_out; + + deflate(&strm, Z_FINISH); + + //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); + int start = file_buffer.size(); + file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); + for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++) { + file_buffer.write[start + i] = strm_out[i]; + } + + deflateEnd(&strm); + meta.compressed_size = strm.total_out; + + } else { + meta.compressed_size = p_len; + } + + // Calculate file CRC-32 + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, p_buffer, p_len); + meta.file_crc32 = crc; + + // Create file header + Vector<uint8_t> file_header = make_file_header(meta); + meta.lfh_size = file_header.size(); + + // Store the header and file; + package->store_buffer(file_header.ptr(), file_header.size()); + package->store_buffer(file_buffer.ptr(), file_buffer.size()); + + file_metadata.push_back(meta); + + return OK; +} + +void AppxPackager::finish() { + // Create and add block map file + EditorNode::progress_task_step("export", "Creating block map...", 4); + + const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml"); + make_block_map(tmp_blockmap_file_path); + + FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); + Vector<uint8_t> blockmap_buffer; + blockmap_buffer.resize(blockmap_file->get_length()); + + blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size()); + + add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); + + blockmap_file->close(); + memdelete(blockmap_file); + + // Add content types + + EditorNode::progress_task_step("export", "Setting content types...", 5); + + const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml"); + make_content_types(tmp_content_types_file_path); + + FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); + Vector<uint8_t> types_buffer; + types_buffer.resize(types_file->get_length()); + + types_file->get_buffer(types_buffer.ptrw(), types_buffer.size()); + + add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); + + types_file->close(); + memdelete(types_file); + + // Cleanup generated files. + DirAccess::remove_file_or_error(tmp_blockmap_file_path); + DirAccess::remove_file_or_error(tmp_content_types_file_path); + + // Pre-process central directory before signing + for (int i = 0; i < file_metadata.size(); i++) { + store_central_dir_header(file_metadata[i]); + } + + // Write central directory + EditorNode::progress_task_step("export", "Finishing package...", 6); + central_dir_offset = package->get_position(); + package->store_buffer(central_dir_data.ptr(), central_dir_data.size()); + + // End record + end_of_central_dir_offset = package->get_position(); + Vector<uint8_t> end_record = make_end_of_central_record(); + package->store_buffer(end_record.ptr(), end_record.size()); + + package->close(); + memdelete(package); + package = nullptr; +} + +AppxPackager::AppxPackager() {} + +AppxPackager::~AppxPackager() {} diff --git a/platform/uwp/export/app_packager.h b/platform/uwp/export/app_packager.h new file mode 100644 index 0000000000..0eb1a78df1 --- /dev/null +++ b/platform/uwp/export/app_packager.h @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* app_packager.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 UWP_APP_PACKAGER_H +#define UWP_APP_PACKAGER_H + +#include "core/config/project_settings.h" +#include "core/core_bind.h" +#include "core/crypto/crypto_core.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/object/class_db.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" + +#include "thirdparty/minizip/unzip.h" +#include "thirdparty/minizip/zip.h" + +#include <zlib.h> + +class AppxPackager { + enum { + FILE_HEADER_MAGIC = 0x04034b50, + DATA_DESCRIPTOR_MAGIC = 0x08074b50, + CENTRAL_DIR_MAGIC = 0x02014b50, + END_OF_CENTRAL_DIR_MAGIC = 0x06054b50, + ZIP64_END_OF_CENTRAL_DIR_MAGIC = 0x06064b50, + ZIP64_END_DIR_LOCATOR_MAGIC = 0x07064b50, + P7X_SIGNATURE = 0x58434b50, + ZIP64_HEADER_ID = 0x0001, + ZIP_VERSION = 20, + ZIP_ARCHIVE_VERSION = 45, + GENERAL_PURPOSE = 0x00, + BASE_FILE_HEADER_SIZE = 30, + DATA_DESCRIPTOR_SIZE = 24, + BASE_CENTRAL_DIR_SIZE = 46, + EXTRA_FIELD_LENGTH = 28, + ZIP64_HEADER_SIZE = 24, + ZIP64_END_OF_CENTRAL_DIR_SIZE = (56 - 12), + END_OF_CENTRAL_DIR_SIZE = 42, + BLOCK_SIZE = 65536, + }; + + struct BlockHash { + String base64_hash; + size_t compressed_size = 0; + }; + + struct FileMeta { + String name; + int lfh_size = 0; + bool compressed = false; + size_t compressed_size = 0; + size_t uncompressed_size = 0; + Vector<BlockHash> hashes; + uLong file_crc32 = 0; + ZPOS64_T zip_offset = 0; + }; + + String progress_task; + FileAccess *package = nullptr; + + Set<String> mime_types; + + Vector<FileMeta> file_metadata; + + ZPOS64_T central_dir_offset; + ZPOS64_T end_of_central_dir_offset; + Vector<uint8_t> central_dir_data; + + String hash_block(const uint8_t *p_block_data, size_t p_block_len); + + void make_block_map(const String &p_path); + void make_content_types(const String &p_path); + + _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t *p_buf) { + for (int i = 0; i < 2; i++) { + *p_buf++ = (p_val >> (i * 8)) & 0xFF; + } + return 2; + } + + _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t *p_buf) { + for (int i = 0; i < 4; i++) { + *p_buf++ = (p_val >> (i * 8)) & 0xFF; + } + return 4; + } + + _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t *p_buf) { + for (int i = 0; i < 8; i++) { + *p_buf++ = (p_val >> (i * 8)) & 0xFF; + } + return 8; + } + + _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t *p_buf) { + for (int i = 0; i < p_val.length(); i++) { + *p_buf++ = p_val.utf8().get(i); + } + return p_val.length(); + } + + Vector<uint8_t> make_file_header(FileMeta p_file_meta); + void store_central_dir_header(const FileMeta &p_file, bool p_do_hash = true); + Vector<uint8_t> make_end_of_central_record(); + + String content_type(String p_extension); + +public: + void set_progress_task(String p_task) { progress_task = p_task; } + void init(FileAccess *p_fa); + Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); + void finish(); + + AppxPackager(); + ~AppxPackager(); +}; + +#endif diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 98925fd7fa..f5c3db33bb 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -30,1408 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/crypto/crypto_core.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/io/zip_io.h" -#include "core/object/class_db.h" -#include "core/version.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "platform/uwp/logo.gen.h" - -#include "thirdparty/minizip/unzip.h" -#include "thirdparty/minizip/zip.h" - -#include <zlib.h> - -// Capabilities -static const char *uwp_capabilities[] = { - "allJoyn", - "codeGeneration", - "internetClient", - "internetClientServer", - "privateNetworkClientServer", - nullptr -}; -static const char *uwp_uap_capabilities[] = { - "appointments", - "blockedChatMessages", - "chat", - "contacts", - "enterpriseAuthentication", - "musicLibrary", - "objects3D", - "picturesLibrary", - "phoneCall", - "removableStorage", - "sharedUserCertificates", - "userAccountInformation", - "videosLibrary", - "voipCall", - nullptr -}; -static const char *uwp_device_capabilities[] = { - "bluetooth", - "location", - "microphone", - "proximity", - "webcam", - nullptr -}; - -class AppxPackager { - enum { - FILE_HEADER_MAGIC = 0x04034b50, - DATA_DESCRIPTOR_MAGIC = 0x08074b50, - CENTRAL_DIR_MAGIC = 0x02014b50, - END_OF_CENTRAL_DIR_MAGIC = 0x06054b50, - ZIP64_END_OF_CENTRAL_DIR_MAGIC = 0x06064b50, - ZIP64_END_DIR_LOCATOR_MAGIC = 0x07064b50, - P7X_SIGNATURE = 0x58434b50, - ZIP64_HEADER_ID = 0x0001, - ZIP_VERSION = 20, - ZIP_ARCHIVE_VERSION = 45, - GENERAL_PURPOSE = 0x00, - BASE_FILE_HEADER_SIZE = 30, - DATA_DESCRIPTOR_SIZE = 24, - BASE_CENTRAL_DIR_SIZE = 46, - EXTRA_FIELD_LENGTH = 28, - ZIP64_HEADER_SIZE = 24, - ZIP64_END_OF_CENTRAL_DIR_SIZE = (56 - 12), - END_OF_CENTRAL_DIR_SIZE = 42, - BLOCK_SIZE = 65536, - }; - - struct BlockHash { - String base64_hash; - size_t compressed_size = 0; - }; - - struct FileMeta { - String name; - int lfh_size = 0; - bool compressed = false; - size_t compressed_size = 0; - size_t uncompressed_size = 0; - Vector<BlockHash> hashes; - uLong file_crc32 = 0; - ZPOS64_T zip_offset = 0; - }; - - String progress_task; - FileAccess *package = nullptr; - - Set<String> mime_types; - - Vector<FileMeta> file_metadata; - - ZPOS64_T central_dir_offset; - ZPOS64_T end_of_central_dir_offset; - Vector<uint8_t> central_dir_data; - - String hash_block(const uint8_t *p_block_data, size_t p_block_len); - - void make_block_map(const String &p_path); - void make_content_types(const String &p_path); - - _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 2; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 2; - } - - _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 4; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 4; - } - - _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 8; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 8; - } - - _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t *p_buf) { - for (int i = 0; i < p_val.length(); i++) { - *p_buf++ = p_val.utf8().get(i); - } - return p_val.length(); - } - - Vector<uint8_t> make_file_header(FileMeta p_file_meta); - void store_central_dir_header(const FileMeta &p_file, bool p_do_hash = true); - Vector<uint8_t> make_end_of_central_record(); - - String content_type(String p_extension); - -public: - void set_progress_task(String p_task) { progress_task = p_task; } - void init(FileAccess *p_fa); - Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); - void finish(); - - AppxPackager(); - ~AppxPackager(); -}; - -/////////////////////////////////////////////////////////////////////////// - -String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) { - unsigned char hash[32]; - char base64[45]; - - CryptoCore::sha256(p_block_data, p_block_len, hash); - size_t len = 0; - CryptoCore::b64_encode((unsigned char *)base64, 45, &len, (unsigned char *)hash, 32); - base64[44] = '\0'; - - return String(base64); -} - -void AppxPackager::make_block_map(const String &p_path) { - FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); - - tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); - tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">"); - - for (int i = 0; i < file_metadata.size(); i++) { - FileMeta file = file_metadata[i]; - - tmp_file->store_string( - "<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); - - for (int j = 0; j < file.hashes.size(); j++) { - tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" "); - if (file.compressed) { - tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" "); - } - tmp_file->store_string("/>"); - } - - tmp_file->store_string("</File>"); - } - - tmp_file->store_string("</BlockMap>"); - - tmp_file->close(); - memdelete(tmp_file); -} - -String AppxPackager::content_type(String p_extension) { - if (p_extension == "png") { - return "image/png"; - } else if (p_extension == "jpg") { - return "image/jpg"; - } else if (p_extension == "xml") { - return "application/xml"; - } else if (p_extension == "exe" || p_extension == "dll") { - return "application/x-msdownload"; - } else { - return "application/octet-stream"; - } -} - -void AppxPackager::make_content_types(const String &p_path) { - FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); - - tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); - - Map<String, String> types; - - for (int i = 0; i < file_metadata.size(); i++) { - String ext = file_metadata[i].name.get_extension().to_lower(); - - if (types.has(ext)) { - continue; - } - - types[ext] = content_type(ext); - - tmp_file->store_string("<Default Extension=\"" + ext + "\" ContentType=\"" + types[ext] + "\" />"); - } - - // Appx signature file - tmp_file->store_string("<Default Extension=\"p7x\" ContentType=\"application/octet-stream\" />"); - - // Override for package files - tmp_file->store_string("<Override PartName=\"/AppxManifest.xml\" ContentType=\"application/vnd.ms-appx.manifest+xml\" />"); - tmp_file->store_string("<Override PartName=\"/AppxBlockMap.xml\" ContentType=\"application/vnd.ms-appx.blockmap+xml\" />"); - tmp_file->store_string("<Override PartName=\"/AppxSignature.p7x\" ContentType=\"application/vnd.ms-appx.signature\" />"); - tmp_file->store_string("<Override PartName=\"/AppxMetadata/CodeIntegrity.cat\" ContentType=\"application/vnd.ms-pkiseccat\" />"); - - tmp_file->store_string("</Types>"); - - tmp_file->close(); - memdelete(tmp_file); -} - -Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { - Vector<uint8_t> buf; - buf.resize(BASE_FILE_HEADER_SIZE + p_file_meta.name.length()); - - int offs = 0; - // Write magic - offs += buf_put_int32(FILE_HEADER_MAGIC, &buf.write[offs]); - - // Version - offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); - - // Special flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); - - // Compression - offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf.write[offs]); - - // File date and time - offs += buf_put_int32(0, &buf.write[offs]); - - // CRC-32 - offs += buf_put_int32(p_file_meta.file_crc32, &buf.write[offs]); - - // Compressed size - offs += buf_put_int32(p_file_meta.compressed_size, &buf.write[offs]); - - // Uncompressed size - offs += buf_put_int32(p_file_meta.uncompressed_size, &buf.write[offs]); - - // File name length - offs += buf_put_int16(p_file_meta.name.length(), &buf.write[offs]); - - // Extra data length - offs += buf_put_int16(0, &buf.write[offs]); - - // File name - offs += buf_put_string(p_file_meta.name, &buf.write[offs]); - - // Done! - return buf; -} - -void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_hash) { - Vector<uint8_t> &buf = central_dir_data; - int offs = buf.size(); - buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); - - // Write magic - offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // ZIP versions - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); - - // General purpose flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); - - // Compression - offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf.write[offs]); - - // Modification date/time - offs += buf_put_int32(0, &buf.write[offs]); - - // Crc-32 - offs += buf_put_int32(p_file.file_crc32, &buf.write[offs]); - - // File sizes - offs += buf_put_int32(p_file.compressed_size, &buf.write[offs]); - offs += buf_put_int32(p_file.uncompressed_size, &buf.write[offs]); - - // File name length - offs += buf_put_int16(p_file.name.length(), &buf.write[offs]); - - // Extra field length - offs += buf_put_int16(0, &buf.write[offs]); - - // Comment length - offs += buf_put_int16(0, &buf.write[offs]); - - // Disk number start, internal/external file attributes - for (int i = 0; i < 8; i++) { - buf.write[offs++] = 0; - } - - // Relative offset - offs += buf_put_int32(p_file.zip_offset, &buf.write[offs]); - - // File name - offs += buf_put_string(p_file.name, &buf.write[offs]); - - // Done! -} - -Vector<uint8_t> AppxPackager::make_end_of_central_record() { - Vector<uint8_t> buf; - buf.resize(ZIP64_END_OF_CENTRAL_DIR_SIZE + 12 + END_OF_CENTRAL_DIR_SIZE); // Size plus magic - - int offs = 0; - - // Write magic - offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // Size of this record - offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf.write[offs]); - - // Version (yes, twice) - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - - // Disk number - for (int i = 0; i < 8; i++) { - buf.write[offs++] = 0; - } - - // Number of entries (total and per disk) - offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); - offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); - - // Size of central dir - offs += buf_put_int64(central_dir_data.size(), &buf.write[offs]); - - // Central dir offset - offs += buf_put_int64(central_dir_offset, &buf.write[offs]); - - ////// ZIP64 locator - - // Write magic for zip64 central dir locator - offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf.write[offs]); - - // Disk number - for (int i = 0; i < 4; i++) { - buf.write[offs++] = 0; - } - - // Relative offset - offs += buf_put_int64(end_of_central_dir_offset, &buf.write[offs]); - - // Number of disks - offs += buf_put_int32(1, &buf.write[offs]); - - /////// End of zip directory - - // Write magic for end central dir - offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // Dummy stuff for Zip64 - for (int i = 0; i < 4; i++) { - buf.write[offs++] = 0x0; - } - for (int i = 0; i < 12; i++) { - buf.write[offs++] = 0xFF; - } - - // Size of comments - for (int i = 0; i < 2; i++) { - buf.write[offs++] = 0; - } - - // Done! - return buf; -} - -void AppxPackager::init(FileAccess *p_fa) { - package = p_fa; - central_dir_offset = 0; - end_of_central_dir_offset = 0; -} - -Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { - if (p_file_no >= 1 && p_total_files >= 1) { - if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) { - return ERR_SKIP; - } - } - - FileMeta meta; - meta.name = p_file_name; - meta.uncompressed_size = p_len; - meta.compressed_size = p_len; - meta.compressed = p_compress; - meta.zip_offset = package->get_position(); - - Vector<uint8_t> file_buffer; - - // Data for compression - z_stream strm; - FileAccess *strm_f = nullptr; - Vector<uint8_t> strm_in; - strm_in.resize(BLOCK_SIZE); - Vector<uint8_t> strm_out; - - if (p_compress) { - strm.zalloc = zipio_alloc; - strm.zfree = zipio_free; - strm.opaque = &strm_f; - - strm_out.resize(BLOCK_SIZE + 8); - - deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); - } - - int step = 0; - - while (p_len - step > 0) { - size_t block_size = (p_len - step) > BLOCK_SIZE ? (size_t)BLOCK_SIZE : (p_len - step); - - for (uint64_t i = 0; i < block_size; i++) { - strm_in.write[i] = p_buffer[step + i]; - } - - BlockHash bh; - bh.base64_hash = hash_block(strm_in.ptr(), block_size); - - if (p_compress) { - strm.avail_in = block_size; - strm.avail_out = strm_out.size(); - strm.next_in = (uint8_t *)strm_in.ptr(); - strm.next_out = strm_out.ptrw(); - - int total_out_before = strm.total_out; - - int err = deflate(&strm, Z_FULL_FLUSH); - ERR_FAIL_COND_V(err < 0, ERR_BUG); // Negative means bug - - bh.compressed_size = strm.total_out - total_out_before; - - //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + bh.compressed_size); - for (uint64_t i = 0; i < bh.compressed_size; i++) { - file_buffer.write[start + i] = strm_out[i]; - } - } else { - bh.compressed_size = block_size; - //package->store_buffer(strm_in.ptr(), block_size); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + block_size); - for (uint64_t i = 0; i < bh.compressed_size; i++) { - file_buffer.write[start + i] = strm_in[i]; - } - } - - meta.hashes.push_back(bh); - - step += block_size; - } - - if (p_compress) { - strm.avail_in = 0; - strm.avail_out = strm_out.size(); - strm.next_in = (uint8_t *)strm_in.ptr(); - strm.next_out = strm_out.ptrw(); - - int total_out_before = strm.total_out; - - deflate(&strm, Z_FINISH); - - //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); - for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++) { - file_buffer.write[start + i] = strm_out[i]; - } - - deflateEnd(&strm); - meta.compressed_size = strm.total_out; - - } else { - meta.compressed_size = p_len; - } - - // Calculate file CRC-32 - uLong crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, p_buffer, p_len); - meta.file_crc32 = crc; - - // Create file header - Vector<uint8_t> file_header = make_file_header(meta); - meta.lfh_size = file_header.size(); - - // Store the header and file; - package->store_buffer(file_header.ptr(), file_header.size()); - package->store_buffer(file_buffer.ptr(), file_buffer.size()); - - file_metadata.push_back(meta); - - return OK; -} - -void AppxPackager::finish() { - // Create and add block map file - EditorNode::progress_task_step("export", "Creating block map...", 4); - - const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml"); - make_block_map(tmp_blockmap_file_path); - - FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); - Vector<uint8_t> blockmap_buffer; - blockmap_buffer.resize(blockmap_file->get_length()); - - blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size()); - - add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); - - blockmap_file->close(); - memdelete(blockmap_file); - - // Add content types - - EditorNode::progress_task_step("export", "Setting content types...", 5); - - const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml"); - make_content_types(tmp_content_types_file_path); - - FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); - Vector<uint8_t> types_buffer; - types_buffer.resize(types_file->get_length()); - - types_file->get_buffer(types_buffer.ptrw(), types_buffer.size()); - - add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); - - types_file->close(); - memdelete(types_file); - - // Cleanup generated files. - DirAccess::remove_file_or_error(tmp_blockmap_file_path); - DirAccess::remove_file_or_error(tmp_content_types_file_path); - - // Pre-process central directory before signing - for (int i = 0; i < file_metadata.size(); i++) { - store_central_dir_header(file_metadata[i]); - } - - // Write central directory - EditorNode::progress_task_step("export", "Finishing package...", 6); - central_dir_offset = package->get_position(); - package->store_buffer(central_dir_data.ptr(), central_dir_data.size()); - - // End record - end_of_central_dir_offset = package->get_position(); - Vector<uint8_t> end_record = make_end_of_central_record(); - package->store_buffer(end_record.ptr(), end_record.size()); - - package->close(); - memdelete(package); - package = nullptr; -} - -AppxPackager::AppxPackager() {} - -AppxPackager::~AppxPackager() {} - -//////////////////////////////////////////////////////////////////// - -class EditorExportPlatformUWP : public EditorExportPlatform { - GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); - - Ref<ImageTexture> logo; - - enum Platform { - ARM, - X86, - X64 - }; - - bool _valid_resource_name(const String &p_name) const { - if (p_name.is_empty()) { - return false; - } - if (p_name.ends_with(".")) { - return false; - } - - static const char *invalid_names[] = { - "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", - "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", - nullptr - }; - - const char **t = invalid_names; - while (*t) { - if (p_name == *t) { - return false; - } - t++; - } - - return true; - } - - bool _valid_guid(const String &p_guid) const { - Vector<String> parts = p_guid.split("-"); - - if (parts.size() != 5) { - return false; - } - if (parts[0].length() != 8) { - return false; - } - for (int i = 1; i < 4; i++) { - if (parts[i].length() != 4) { - return false; - } - } - if (parts[4].length() != 12) { - return false; - } - - return true; - } - - bool _valid_bgcolor(const String &p_color) const { - if (p_color.is_empty()) { - return true; - } - if (p_color.begins_with("#") && p_color.is_valid_html_color()) { - return true; - } - - // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx - static const char *valid_colors[] = { - "aliceBlue", "antiqueWhite", "aqua", "aquamarine", "azure", "beige", - "bisque", "black", "blanchedAlmond", "blue", "blueViolet", "brown", - "burlyWood", "cadetBlue", "chartreuse", "chocolate", "coral", "cornflowerBlue", - "cornsilk", "crimson", "cyan", "darkBlue", "darkCyan", "darkGoldenrod", - "darkGray", "darkGreen", "darkKhaki", "darkMagenta", "darkOliveGreen", "darkOrange", - "darkOrchid", "darkRed", "darkSalmon", "darkSeaGreen", "darkSlateBlue", "darkSlateGray", - "darkTurquoise", "darkViolet", "deepPink", "deepSkyBlue", "dimGray", "dodgerBlue", - "firebrick", "floralWhite", "forestGreen", "fuchsia", "gainsboro", "ghostWhite", - "gold", "goldenrod", "gray", "green", "greenYellow", "honeydew", - "hotPink", "indianRed", "indigo", "ivory", "khaki", "lavender", - "lavenderBlush", "lawnGreen", "lemonChiffon", "lightBlue", "lightCoral", "lightCyan", - "lightGoldenrodYellow", "lightGreen", "lightGray", "lightPink", "lightSalmon", "lightSeaGreen", - "lightSkyBlue", "lightSlateGray", "lightSteelBlue", "lightYellow", "lime", "limeGreen", - "linen", "magenta", "maroon", "mediumAquamarine", "mediumBlue", "mediumOrchid", - "mediumPurple", "mediumSeaGreen", "mediumSlateBlue", "mediumSpringGreen", "mediumTurquoise", "mediumVioletRed", - "midnightBlue", "mintCream", "mistyRose", "moccasin", "navajoWhite", "navy", - "oldLace", "olive", "oliveDrab", "orange", "orangeRed", "orchid", - "paleGoldenrod", "paleGreen", "paleTurquoise", "paleVioletRed", "papayaWhip", "peachPuff", - "peru", "pink", "plum", "powderBlue", "purple", "red", - "rosyBrown", "royalBlue", "saddleBrown", "salmon", "sandyBrown", "seaGreen", - "seaShell", "sienna", "silver", "skyBlue", "slateBlue", "slateGray", - "snow", "springGreen", "steelBlue", "tan", "teal", "thistle", - "tomato", "transparent", "turquoise", "violet", "wheat", "white", - "whiteSmoke", "yellow", "yellowGreen", - nullptr - }; - - const char **color = valid_colors; - - while (*color) { - if (p_color == *color) { - return true; - } - color++; - } - - return false; - } - - bool _valid_image(const StreamTexture2D *p_image, int p_width, int p_height) const { - if (!p_image) { - return false; - } - - // TODO: Add resource creation or image rescaling to enable other scales: - // 1.25, 1.5, 2.0 - return p_width == p_image->get_width() && p_height == p_image->get_height(); - } - - Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const { - String result = String::utf8((const char *)p_template.ptr(), p_template.size()); - - result = result.replace("$godot_version$", VERSION_FULL_NAME); - - result = result.replace("$identity_name$", p_preset->get("package/unique_name")); - result = result.replace("$publisher$", p_preset->get("package/publisher")); - - result = result.replace("$product_guid$", p_preset->get("identity/product_guid")); - result = result.replace("$publisher_guid$", p_preset->get("identity/publisher_guid")); - - String version = itos(p_preset->get("version/major")) + "." + itos(p_preset->get("version/minor")) + "." + itos(p_preset->get("version/build")) + "." + itos(p_preset->get("version/revision")); - result = result.replace("$version_string$", version); - - Platform arch = (Platform)(int)p_preset->get("architecture/target"); - String architecture = arch == ARM ? "arm" : (arch == X86 ? "x86" : "x64"); - result = result.replace("$architecture$", architecture); - - result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name"))); - - result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); - result = result.replace("$app_description$", p_preset->get("package/description")); - result = result.replace("$bg_color$", p_preset->get("images/background_color")); - result = result.replace("$short_name$", p_preset->get("package/short_name")); - - String name_on_tiles = ""; - if ((bool)p_preset->get("tiles/show_name_on_square150x150")) { - name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; - } - if ((bool)p_preset->get("tiles/show_name_on_wide310x150")) { - name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; - } - if ((bool)p_preset->get("tiles/show_name_on_square310x310")) { - name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; - } - - String show_name_on_tiles = ""; - if (!name_on_tiles.is_empty()) { - show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; - } - - result = result.replace("$name_on_tiles$", name_on_tiles); - - String rotations = ""; - if ((bool)p_preset->get("orientation/landscape")) { - rotations += " <uap:Rotation Preference=\"landscape\" />\n"; - } - if ((bool)p_preset->get("orientation/portrait")) { - rotations += " <uap:Rotation Preference=\"portrait\" />\n"; - } - if ((bool)p_preset->get("orientation/landscape_flipped")) { - rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; - } - if ((bool)p_preset->get("orientation/portrait_flipped")) { - rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; - } - - String rotation_preference = ""; - if (!rotations.is_empty()) { - rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; - } - - result = result.replace("$rotation_preference$", rotation_preference); - - String capabilities_elements = ""; - const char **basic = uwp_capabilities; - while (*basic) { - if ((bool)p_preset->get("capabilities/" + String(*basic))) { - capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; - } - basic++; - } - const char **uap = uwp_uap_capabilities; - while (*uap) { - if ((bool)p_preset->get("capabilities/" + String(*uap))) { - capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; - } - uap++; - } - const char **device = uwp_device_capabilities; - while (*device) { - if ((bool)p_preset->get("capabilities/" + String(*device))) { - capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; - } - device++; - } - - if (!((bool)p_preset->get("capabilities/internetClient")) && p_give_internet) { - capabilities_elements += " <Capability Name=\"internetClient\" />\n"; - } - - String capabilities_string = "<Capabilities />"; - if (!capabilities_elements.is_empty()) { - capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; - } - - result = result.replace("$capabilities_place$", capabilities_string); - - Vector<uint8_t> r_ret; - r_ret.resize(result.length()); - - for (int i = 0; i < result.length(); i++) { - r_ret.write[i] = result.utf8().get(i); - } - - return r_ret; - } - - Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - Vector<uint8_t> data; - StreamTexture2D *texture = nullptr; - - if (p_path.find("StoreLogo") != -1) { - texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo"))); - } else if (p_path.find("Square44x44Logo") != -1) { - texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); - } else if (p_path.find("Square71x71Logo") != -1) { - texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); - } else if (p_path.find("Square150x150Logo") != -1) { - texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); - } else if (p_path.find("Square310x310Logo") != -1) { - texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); - } else if (p_path.find("Wide310x150Logo") != -1) { - texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); - } else if (p_path.find("SplashScreen") != -1) { - texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen"))); - } else { - ERR_PRINT("Unable to load logo"); - } - - if (!texture) { - return data; - } - - String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png"); - - Error err = texture->get_image()->save_png(tmp_path); - - if (err != OK) { - String err_string = "Couldn't save temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_FAIL_V_MSG(data, err_string); - } - - FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err); - - if (err != OK) { - String err_string = "Couldn't open temp logo file."; - // Cleanup generated file. - DirAccess::remove_file_or_error(tmp_path); - EditorNode::add_io_error(err_string); - ERR_FAIL_V_MSG(data, err_string); - } - - data.resize(f->get_length()); - f->get_buffer(data.ptrw(), data.size()); - - f->close(); - memdelete(f); - DirAccess::remove_file_or_error(tmp_path); - - return data; - } - - static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - /* TODO: This was copied verbatim from Android export. It should be - * refactored to the parent class and also be used for .zip export. - */ - - /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected at runtime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ - - // -- Unconditional uncompress to mimic AAPT plus some other - - static const char *unconditional_compress_ext[] = { - // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp - // These formats are already compressed, or don't compress well: - ".jpg", ".jpeg", ".png", ".gif", - ".wav", ".mp2", ".mp3", ".ogg", ".aac", - ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", - ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", - ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", - // Godot-specific: - ".webp", // Same reasoning as .png - ".cfb", // Don't let small config files slow-down startup - ".scn", // Binary scenes are usually already compressed - ".stex", // Streamable textures are usually already compressed - // Trailer for easier processing - nullptr - }; - - for (const char **ext = unconditional_compress_ext; *ext; ++ext) { - if (p_path.to_lower().ends_with(String(*ext))) { - return false; - } - } - - // -- Compressed resource? - - if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { - // Already compressed - return false; - } - - // --- TODO: Decide on texture resources according to their image compression setting - - return true; - } - - static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - AppxPackager *packager = (AppxPackager *)p_userdata; - String dst_path = p_path.replace_first("res://", "game/"); - - return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); - } - -public: - virtual String get_name() const override { - return "UWP"; - } - virtual String get_os_name() const override { - return "UWP"; - } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - list.push_back("appx"); - return list; - } - - virtual Ref<Texture2D> get_logo() const override { - return logo; - } - - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override { - r_features->push_back("s3tc"); - r_features->push_back("etc"); - switch ((int)p_preset->get("architecture/target")) { - case EditorExportPlatformUWP::ARM: { - r_features->push_back("arm"); - } break; - case EditorExportPlatformUWP::X86: { - r_features->push_back("32"); - } break; - case EditorExportPlatformUWP::X64: { - r_features->push_back("64"); - } break; - } - } - - virtual void get_export_options(List<ExportOption> *r_options) override { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game.Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher", PROPERTY_HINT_PLACEHOLDER_TEXT, "CN=CompanyName"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/minor"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/build"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/revision"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent")); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false)); - - // Capabilities - const char **basic = uwp_capabilities; - while (*basic) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false)); - basic++; - } - - const char **uap = uwp_uap_capabilities; - while (*uap) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false)); - uap++; - } - - const char **device = uwp_device_capabilities; - while (*device) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false)); - device++; - } - } - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - Platform arch = (Platform)(int)(p_preset->get("architecture/target")); - String platform_infix; - switch (arch) { - case EditorExportPlatformUWP::ARM: { - platform_infix = "arm"; - } break; - case EditorExportPlatformUWP::X86: { - platform_infix = "x86"; - } break; - case EditorExportPlatformUWP::X64: { - platform_infix = "x64"; - } break; - } - - bool dvalid = exists_export_template("uwp_" + platform_infix + "_debug.zip", &err); - bool rvalid = exists_export_template("uwp_" + platform_infix + "_release.zip", &err); - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - // Validate the rest of the configuration. - - if (!_valid_resource_name(p_preset->get("package/short_name"))) { - valid = false; - err += TTR("Invalid package short name.") + "\n"; - } - - if (!_valid_resource_name(p_preset->get("package/unique_name"))) { - valid = false; - err += TTR("Invalid package unique name.") + "\n"; - } - - if (!_valid_resource_name(p_preset->get("package/publisher_display_name"))) { - valid = false; - err += TTR("Invalid package publisher display name.") + "\n"; - } - - if (!_valid_guid(p_preset->get("identity/product_guid"))) { - valid = false; - err += TTR("Invalid product GUID.") + "\n"; - } - - if (!_valid_guid(p_preset->get("identity/publisher_guid"))) { - valid = false; - err += TTR("Invalid publisher GUID.") + "\n"; - } - - if (!_valid_bgcolor(p_preset->get("images/background_color"))) { - valid = false; - err += TTR("Invalid background color.") + "\n"; - } - - if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) { - valid = false; - err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n"; - } - - if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) { - valid = false; - err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n"; - } - - if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) { - valid = false; - err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n"; - } - - if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) { - valid = false; - err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n"; - } - - if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) { - valid = false; - err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n"; - } - - if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) { - valid = false; - err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n"; - } - - if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) { - valid = false; - err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n"; - } - - r_error = err; - return valid; - } - - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_appx; - - EditorProgress ep("export", "Exporting for UWP", 7, true); - - if (p_debug) { - src_appx = p_preset->get("custom_template/debug"); - } else { - src_appx = p_preset->get("custom_template/release"); - } - - src_appx = src_appx.strip_edges(); - - Platform arch = (Platform)(int)p_preset->get("architecture/target"); - - if (src_appx == "") { - String err, infix; - switch (arch) { - case ARM: { - infix = "_arm_"; - } break; - case X86: { - infix = "_x86_"; - } break; - case X64: { - infix = "_x64_"; - } break; - } - if (p_debug) { - src_appx = find_export_template("uwp" + infix + "debug.zip", &err); - } else { - src_appx = find_export_template("uwp" + infix + "release.zip", &err); - } - if (src_appx == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - Error err = OK; - - FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - - AppxPackager packager; - packager.init(fa_pack); - - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - if (ep.step("Creating package...", 0)) { - return ERR_SKIP; - } - - unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); - - if (!pkg) { - EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(pkg); - - if (ep.step("Copying template files...", 1)) { - return ERR_SKIP; - } - - EditorNode::progress_add_task("template_files", "Template files", 100); - packager.set_progress_task("template_files"); - - int template_files_amount = 9; - int template_file_no = 1; - - while (ret == UNZ_OK) { - // get file name - unz_file_info info; - char fname[16834]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0); - - String path = fname; - - if (path.ends_with("/")) { - // Ignore directories - ret = unzGoToNextFile(pkg); - continue; - } - - Vector<uint8_t> data; - bool do_read = true; - - if (path.begins_with("Assets/")) { - path = path.replace(".scale-100", ""); - - data = _get_image_data(p_preset, path); - if (data.size() > 0) { - do_read = false; - } - } - - //read - if (do_read) { - data.resize(info.uncompressed_size); - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - } - - if (path == "AppxManifest.xml") { - data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); - } - - print_line("ADDING: " + path); - - err = packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); - if (err != OK) { - return err; - } - - ret = unzGoToNextFile(pkg); - } - - EditorNode::progress_end_task("template_files"); - - if (ep.step("Creating command line...", 2)) { - return ERR_SKIP; - } - - Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); - for (int i = 0; i < cl.size(); i++) { - if (cl[i].strip_edges().length() == 0) { - cl.remove(i); - i--; - } - } - - if (!(p_flags & DEBUG_FLAG_DUMB_CLIENT)) { - cl.push_back("--path"); - cl.push_back("game"); - } - - gen_export_flags(cl, p_flags); - - // Command line file - Vector<uint8_t> clf; - - // Argc - clf.resize(4); - encode_uint32(cl.size(), clf.ptrw()); - - for (int i = 0; i < cl.size(); i++) { - CharString txt = cl[i].utf8(); - int base = clf.size(); - clf.resize(base + 4 + txt.length()); - encode_uint32(txt.length(), &clf.write[base]); - memcpy(&clf.write[base + 4], txt.ptr(), txt.length()); - print_line(itos(i) + " param: " + cl[i]); - } - - err = packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); - if (err != OK) { - return err; - } - - if (ep.step("Adding project files...", 3)) { - return ERR_SKIP; - } - - EditorNode::progress_add_task("project_files", "Project Files", 100); - packager.set_progress_task("project_files"); - - err = export_project_files(p_preset, save_appx_file, &packager); - - EditorNode::progress_end_task("project_files"); - - if (ep.step("Closing package...", 7)) { - return ERR_SKIP; - } - - unzClose(pkg); - - packager.finish(); - -#ifdef WINDOWS_ENABLED - // Sign with signtool - String signtool_path = EditorSettings::get_singleton()->get("export/uwp/signtool"); - if (signtool_path == String()) { - return OK; - } - - if (!FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - - static String algs[] = { "MD5", "SHA1", "SHA256" }; - - String cert_path = EditorSettings::get_singleton()->get("export/uwp/debug_certificate"); - String cert_pass = EditorSettings::get_singleton()->get("export/uwp/debug_password"); - int cert_alg = EditorSettings::get_singleton()->get("export/uwp/debug_algorithm"); - - if (!p_debug) { - cert_path = p_preset->get("signing/certificate"); - cert_pass = p_preset->get("signing/password"); - cert_alg = p_preset->get("signing/algorithm"); - } - - if (cert_path == String()) { - return OK; // Certificate missing, don't try to sign - } - - if (!FileAccess::exists(cert_path)) { - ERR_PRINT("Could not find certificate file at " + cert_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - - if (cert_alg < 0 || cert_alg > 2) { - ERR_PRINT("Invalid certificate algorithm " + itos(cert_alg) + ", aborting."); - return ERR_INVALID_DATA; - } - - List<String> args; - args.push_back("sign"); - args.push_back("/fd"); - args.push_back(algs[cert_alg]); - args.push_back("/a"); - args.push_back("/f"); - args.push_back(cert_path); - args.push_back("/p"); - args.push_back(cert_pass); - args.push_back(p_path); - - OS::get_singleton()->execute(signtool_path, args); -#endif // WINDOWS_ENABLED - - return OK; - } - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("pc"); - r_features->push_back("UWP"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformUWP() { - Ref<Image> img = memnew(Image(_uwp_logo)); - logo.instantiate(); - logo->create_from_image(img); - } -}; +#include "export_plugin.h" void register_uwp_exporter() { #ifdef WINDOWS_ENABLED diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp new file mode 100644 index 0000000000..5bd00b1549 --- /dev/null +++ b/platform/uwp/export/export_plugin.cpp @@ -0,0 +1,498 @@ +/*************************************************************************/ +/* export_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 "export_plugin.h" + +#include "platform/uwp/logo.gen.h" + +String EditorExportPlatformUWP::get_name() const { + return "UWP"; +} +String EditorExportPlatformUWP::get_os_name() const { + return "UWP"; +} + +List<String> EditorExportPlatformUWP::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("appx"); + return list; +} + +Ref<Texture2D> EditorExportPlatformUWP::get_logo() const { + return logo; +} + +void EditorExportPlatformUWP::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + r_features->push_back("s3tc"); + r_features->push_back("etc"); + switch ((int)p_preset->get("architecture/target")) { + case EditorExportPlatformUWP::ARM: { + r_features->push_back("arm"); + } break; + case EditorExportPlatformUWP::X86: { + r_features->push_back("32"); + } break; + case EditorExportPlatformUWP::X64: { + r_features->push_back("64"); + } break; + } +} + +void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game.Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher", PROPERTY_HINT_PLACEHOLDER_TEXT, "CN=CompanyName"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/minor"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/build"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/revision"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent")); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false)); + + // Capabilities + const char **basic = uwp_capabilities; + while (*basic) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false)); + basic++; + } + + const char **uap = uwp_uap_capabilities; + while (*uap) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false)); + uap++; + } + + const char **device = uwp_device_capabilities; + while (*device) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false)); + device++; + } +} + +bool EditorExportPlatformUWP::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + Platform arch = (Platform)(int)(p_preset->get("architecture/target")); + String platform_infix; + switch (arch) { + case EditorExportPlatformUWP::ARM: { + platform_infix = "arm"; + } break; + case EditorExportPlatformUWP::X86: { + platform_infix = "x86"; + } break; + case EditorExportPlatformUWP::X64: { + platform_infix = "x64"; + } break; + } + + bool dvalid = exists_export_template("uwp_" + platform_infix + "_debug.zip", &err); + bool rvalid = exists_export_template("uwp_" + platform_infix + "_release.zip", &err); + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + // Validate the rest of the configuration. + + if (!_valid_resource_name(p_preset->get("package/short_name"))) { + valid = false; + err += TTR("Invalid package short name.") + "\n"; + } + + if (!_valid_resource_name(p_preset->get("package/unique_name"))) { + valid = false; + err += TTR("Invalid package unique name.") + "\n"; + } + + if (!_valid_resource_name(p_preset->get("package/publisher_display_name"))) { + valid = false; + err += TTR("Invalid package publisher display name.") + "\n"; + } + + if (!_valid_guid(p_preset->get("identity/product_guid"))) { + valid = false; + err += TTR("Invalid product GUID.") + "\n"; + } + + if (!_valid_guid(p_preset->get("identity/publisher_guid"))) { + valid = false; + err += TTR("Invalid publisher GUID.") + "\n"; + } + + if (!_valid_bgcolor(p_preset->get("images/background_color"))) { + valid = false; + err += TTR("Invalid background color.") + "\n"; + } + + if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) { + valid = false; + err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n"; + } + + if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) { + valid = false; + err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n"; + } + + if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) { + valid = false; + err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n"; + } + + if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) { + valid = false; + err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n"; + } + + if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) { + valid = false; + err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n"; + } + + if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) { + valid = false; + err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n"; + } + + if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) { + valid = false; + err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n"; + } + + r_error = err; + return valid; +} + +Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_appx; + + EditorProgress ep("export", "Exporting for UWP", 7, true); + + if (p_debug) { + src_appx = p_preset->get("custom_template/debug"); + } else { + src_appx = p_preset->get("custom_template/release"); + } + + src_appx = src_appx.strip_edges(); + + Platform arch = (Platform)(int)p_preset->get("architecture/target"); + + if (src_appx == "") { + String err, infix; + switch (arch) { + case ARM: { + infix = "_arm_"; + } break; + case X86: { + infix = "_x86_"; + } break; + case X64: { + infix = "_x64_"; + } break; + } + if (p_debug) { + src_appx = find_export_template("uwp" + infix + "debug.zip", &err); + } else { + src_appx = find_export_template("uwp" + infix + "release.zip", &err); + } + if (src_appx == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + + Error err = OK; + + FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + + AppxPackager packager; + packager.init(fa_pack); + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + if (ep.step("Creating package...", 0)) { + return ERR_SKIP; + } + + unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); + + if (!pkg) { + EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(pkg); + + if (ep.step("Copying template files...", 1)) { + return ERR_SKIP; + } + + EditorNode::progress_add_task("template_files", "Template files", 100); + packager.set_progress_task("template_files"); + + int template_files_amount = 9; + int template_file_no = 1; + + while (ret == UNZ_OK) { + // get file name + unz_file_info info; + char fname[16834]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0); + + String path = fname; + + if (path.ends_with("/")) { + // Ignore directories + ret = unzGoToNextFile(pkg); + continue; + } + + Vector<uint8_t> data; + bool do_read = true; + + if (path.begins_with("Assets/")) { + path = path.replace(".scale-100", ""); + + data = _get_image_data(p_preset, path); + if (data.size() > 0) { + do_read = false; + } + } + + //read + if (do_read) { + data.resize(info.uncompressed_size); + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + } + + if (path == "AppxManifest.xml") { + data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); + } + + print_line("ADDING: " + path); + + err = packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); + if (err != OK) { + return err; + } + + ret = unzGoToNextFile(pkg); + } + + EditorNode::progress_end_task("template_files"); + + if (ep.step("Creating command line...", 2)) { + return ERR_SKIP; + } + + Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); + for (int i = 0; i < cl.size(); i++) { + if (cl[i].strip_edges().length() == 0) { + cl.remove(i); + i--; + } + } + + if (!(p_flags & DEBUG_FLAG_DUMB_CLIENT)) { + cl.push_back("--path"); + cl.push_back("game"); + } + + gen_export_flags(cl, p_flags); + + // Command line file + Vector<uint8_t> clf; + + // Argc + clf.resize(4); + encode_uint32(cl.size(), clf.ptrw()); + + for (int i = 0; i < cl.size(); i++) { + CharString txt = cl[i].utf8(); + int base = clf.size(); + clf.resize(base + 4 + txt.length()); + encode_uint32(txt.length(), &clf.write[base]); + memcpy(&clf.write[base + 4], txt.ptr(), txt.length()); + print_line(itos(i) + " param: " + cl[i]); + } + + err = packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); + if (err != OK) { + return err; + } + + if (ep.step("Adding project files...", 3)) { + return ERR_SKIP; + } + + EditorNode::progress_add_task("project_files", "Project Files", 100); + packager.set_progress_task("project_files"); + + err = export_project_files(p_preset, save_appx_file, &packager); + + EditorNode::progress_end_task("project_files"); + + if (ep.step("Closing package...", 7)) { + return ERR_SKIP; + } + + unzClose(pkg); + + packager.finish(); + +#ifdef WINDOWS_ENABLED + // Sign with signtool + String signtool_path = EditorSettings::get_singleton()->get("export/uwp/signtool"); + if (signtool_path == String()) { + return OK; + } + + if (!FileAccess::exists(signtool_path)) { + ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + static String algs[] = { "MD5", "SHA1", "SHA256" }; + + String cert_path = EditorSettings::get_singleton()->get("export/uwp/debug_certificate"); + String cert_pass = EditorSettings::get_singleton()->get("export/uwp/debug_password"); + int cert_alg = EditorSettings::get_singleton()->get("export/uwp/debug_algorithm"); + + if (!p_debug) { + cert_path = p_preset->get("signing/certificate"); + cert_pass = p_preset->get("signing/password"); + cert_alg = p_preset->get("signing/algorithm"); + } + + if (cert_path == String()) { + return OK; // Certificate missing, don't try to sign + } + + if (!FileAccess::exists(cert_path)) { + ERR_PRINT("Could not find certificate file at " + cert_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + if (cert_alg < 0 || cert_alg > 2) { + ERR_PRINT("Invalid certificate algorithm " + itos(cert_alg) + ", aborting."); + return ERR_INVALID_DATA; + } + + List<String> args; + args.push_back("sign"); + args.push_back("/fd"); + args.push_back(algs[cert_alg]); + args.push_back("/a"); + args.push_back("/f"); + args.push_back(cert_path); + args.push_back("/p"); + args.push_back(cert_pass); + args.push_back(p_path); + + OS::get_singleton()->execute(signtool_path, args); +#endif // WINDOWS_ENABLED + + return OK; +} + +void EditorExportPlatformUWP::get_platform_features(List<String> *r_features) { + r_features->push_back("pc"); + r_features->push_back("UWP"); +} + +void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { +} + +EditorExportPlatformUWP::EditorExportPlatformUWP() { + Ref<Image> img = memnew(Image(_uwp_logo)); + logo.instantiate(); + logo->create_from_image(img); +} diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h new file mode 100644 index 0000000000..f295789254 --- /dev/null +++ b/platform/uwp/export/export_plugin.h @@ -0,0 +1,448 @@ +/*************************************************************************/ +/* export_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 UWP_EXPORT_PLUGIN_H +#define UWP_EXPORT_PLUGIN_H + +#include "core/config/project_settings.h" +#include "core/crypto/crypto_core.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/object/class_db.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" + +#include "thirdparty/minizip/unzip.h" +#include "thirdparty/minizip/zip.h" + +#include "app_packager.h" + +#include <zlib.h> + +// Capabilities +static const char *uwp_capabilities[] = { + "allJoyn", + "codeGeneration", + "internetClient", + "internetClientServer", + "privateNetworkClientServer", + nullptr +}; +static const char *uwp_uap_capabilities[] = { + "appointments", + "blockedChatMessages", + "chat", + "contacts", + "enterpriseAuthentication", + "musicLibrary", + "objects3D", + "picturesLibrary", + "phoneCall", + "removableStorage", + "sharedUserCertificates", + "userAccountInformation", + "videosLibrary", + "voipCall", + nullptr +}; +static const char *uwp_device_capabilities[] = { + "bluetooth", + "location", + "microphone", + "proximity", + "webcam", + nullptr +}; + +class EditorExportPlatformUWP : public EditorExportPlatform { + GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); + + Ref<ImageTexture> logo; + + enum Platform { + ARM, + X86, + X64 + }; + + bool _valid_resource_name(const String &p_name) const { + if (p_name.is_empty()) { + return false; + } + if (p_name.ends_with(".")) { + return false; + } + + static const char *invalid_names[] = { + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", + "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + nullptr + }; + + const char **t = invalid_names; + while (*t) { + if (p_name == *t) { + return false; + } + t++; + } + + return true; + } + + bool _valid_guid(const String &p_guid) const { + Vector<String> parts = p_guid.split("-"); + + if (parts.size() != 5) { + return false; + } + if (parts[0].length() != 8) { + return false; + } + for (int i = 1; i < 4; i++) { + if (parts[i].length() != 4) { + return false; + } + } + if (parts[4].length() != 12) { + return false; + } + + return true; + } + + bool _valid_bgcolor(const String &p_color) const { + if (p_color.is_empty()) { + return true; + } + if (p_color.begins_with("#") && p_color.is_valid_html_color()) { + return true; + } + + // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx + static const char *valid_colors[] = { + "aliceBlue", "antiqueWhite", "aqua", "aquamarine", "azure", "beige", + "bisque", "black", "blanchedAlmond", "blue", "blueViolet", "brown", + "burlyWood", "cadetBlue", "chartreuse", "chocolate", "coral", "cornflowerBlue", + "cornsilk", "crimson", "cyan", "darkBlue", "darkCyan", "darkGoldenrod", + "darkGray", "darkGreen", "darkKhaki", "darkMagenta", "darkOliveGreen", "darkOrange", + "darkOrchid", "darkRed", "darkSalmon", "darkSeaGreen", "darkSlateBlue", "darkSlateGray", + "darkTurquoise", "darkViolet", "deepPink", "deepSkyBlue", "dimGray", "dodgerBlue", + "firebrick", "floralWhite", "forestGreen", "fuchsia", "gainsboro", "ghostWhite", + "gold", "goldenrod", "gray", "green", "greenYellow", "honeydew", + "hotPink", "indianRed", "indigo", "ivory", "khaki", "lavender", + "lavenderBlush", "lawnGreen", "lemonChiffon", "lightBlue", "lightCoral", "lightCyan", + "lightGoldenrodYellow", "lightGreen", "lightGray", "lightPink", "lightSalmon", "lightSeaGreen", + "lightSkyBlue", "lightSlateGray", "lightSteelBlue", "lightYellow", "lime", "limeGreen", + "linen", "magenta", "maroon", "mediumAquamarine", "mediumBlue", "mediumOrchid", + "mediumPurple", "mediumSeaGreen", "mediumSlateBlue", "mediumSpringGreen", "mediumTurquoise", "mediumVioletRed", + "midnightBlue", "mintCream", "mistyRose", "moccasin", "navajoWhite", "navy", + "oldLace", "olive", "oliveDrab", "orange", "orangeRed", "orchid", + "paleGoldenrod", "paleGreen", "paleTurquoise", "paleVioletRed", "papayaWhip", "peachPuff", + "peru", "pink", "plum", "powderBlue", "purple", "red", + "rosyBrown", "royalBlue", "saddleBrown", "salmon", "sandyBrown", "seaGreen", + "seaShell", "sienna", "silver", "skyBlue", "slateBlue", "slateGray", + "snow", "springGreen", "steelBlue", "tan", "teal", "thistle", + "tomato", "transparent", "turquoise", "violet", "wheat", "white", + "whiteSmoke", "yellow", "yellowGreen", + nullptr + }; + + const char **color = valid_colors; + + while (*color) { + if (p_color == *color) { + return true; + } + color++; + } + + return false; + } + + bool _valid_image(const StreamTexture2D *p_image, int p_width, int p_height) const { + if (!p_image) { + return false; + } + + // TODO: Add resource creation or image rescaling to enable other scales: + // 1.25, 1.5, 2.0 + return p_width == p_image->get_width() && p_height == p_image->get_height(); + } + + Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const { + String result = String::utf8((const char *)p_template.ptr(), p_template.size()); + + result = result.replace("$godot_version$", VERSION_FULL_NAME); + + result = result.replace("$identity_name$", p_preset->get("package/unique_name")); + result = result.replace("$publisher$", p_preset->get("package/publisher")); + + result = result.replace("$product_guid$", p_preset->get("identity/product_guid")); + result = result.replace("$publisher_guid$", p_preset->get("identity/publisher_guid")); + + String version = itos(p_preset->get("version/major")) + "." + itos(p_preset->get("version/minor")) + "." + itos(p_preset->get("version/build")) + "." + itos(p_preset->get("version/revision")); + result = result.replace("$version_string$", version); + + Platform arch = (Platform)(int)p_preset->get("architecture/target"); + String architecture = arch == ARM ? "arm" : (arch == X86 ? "x86" : "x64"); + result = result.replace("$architecture$", architecture); + + result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name"))); + + result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); + result = result.replace("$app_description$", p_preset->get("package/description")); + result = result.replace("$bg_color$", p_preset->get("images/background_color")); + result = result.replace("$short_name$", p_preset->get("package/short_name")); + + String name_on_tiles = ""; + if ((bool)p_preset->get("tiles/show_name_on_square150x150")) { + name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; + } + if ((bool)p_preset->get("tiles/show_name_on_wide310x150")) { + name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; + } + if ((bool)p_preset->get("tiles/show_name_on_square310x310")) { + name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; + } + + String show_name_on_tiles = ""; + if (!name_on_tiles.is_empty()) { + show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; + } + + result = result.replace("$name_on_tiles$", name_on_tiles); + + String rotations = ""; + if ((bool)p_preset->get("orientation/landscape")) { + rotations += " <uap:Rotation Preference=\"landscape\" />\n"; + } + if ((bool)p_preset->get("orientation/portrait")) { + rotations += " <uap:Rotation Preference=\"portrait\" />\n"; + } + if ((bool)p_preset->get("orientation/landscape_flipped")) { + rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; + } + if ((bool)p_preset->get("orientation/portrait_flipped")) { + rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; + } + + String rotation_preference = ""; + if (!rotations.is_empty()) { + rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; + } + + result = result.replace("$rotation_preference$", rotation_preference); + + String capabilities_elements = ""; + const char **basic = uwp_capabilities; + while (*basic) { + if ((bool)p_preset->get("capabilities/" + String(*basic))) { + capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; + } + basic++; + } + const char **uap = uwp_uap_capabilities; + while (*uap) { + if ((bool)p_preset->get("capabilities/" + String(*uap))) { + capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; + } + uap++; + } + const char **device = uwp_device_capabilities; + while (*device) { + if ((bool)p_preset->get("capabilities/" + String(*device))) { + capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; + } + device++; + } + + if (!((bool)p_preset->get("capabilities/internetClient")) && p_give_internet) { + capabilities_elements += " <Capability Name=\"internetClient\" />\n"; + } + + String capabilities_string = "<Capabilities />"; + if (!capabilities_elements.is_empty()) { + capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; + } + + result = result.replace("$capabilities_place$", capabilities_string); + + Vector<uint8_t> r_ret; + r_ret.resize(result.length()); + + for (int i = 0; i < result.length(); i++) { + r_ret.write[i] = result.utf8().get(i); + } + + return r_ret; + } + + Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + Vector<uint8_t> data; + StreamTexture2D *texture = nullptr; + + if (p_path.find("StoreLogo") != -1) { + texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo"))); + } else if (p_path.find("Square44x44Logo") != -1) { + texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); + } else if (p_path.find("Square71x71Logo") != -1) { + texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); + } else if (p_path.find("Square150x150Logo") != -1) { + texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); + } else if (p_path.find("Square310x310Logo") != -1) { + texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); + } else if (p_path.find("Wide310x150Logo") != -1) { + texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); + } else if (p_path.find("SplashScreen") != -1) { + texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen"))); + } else { + ERR_PRINT("Unable to load logo"); + } + + if (!texture) { + return data; + } + + String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png"); + + Error err = texture->get_image()->save_png(tmp_path); + + if (err != OK) { + String err_string = "Couldn't save temp logo file."; + + EditorNode::add_io_error(err_string); + ERR_FAIL_V_MSG(data, err_string); + } + + FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err); + + if (err != OK) { + String err_string = "Couldn't open temp logo file."; + // Cleanup generated file. + DirAccess::remove_file_or_error(tmp_path); + EditorNode::add_io_error(err_string); + ERR_FAIL_V_MSG(data, err_string); + } + + data.resize(f->get_length()); + f->get_buffer(data.ptrw(), data.size()); + + f->close(); + memdelete(f); + DirAccess::remove_file_or_error(tmp_path); + + return data; + } + + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { + /* TODO: This was copied verbatim from Android export. It should be + * refactored to the parent class and also be used for .zip export. + */ + + /* + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected at runtime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ + + // -- Unconditional uncompress to mimic AAPT plus some other + + static const char *unconditional_compress_ext[] = { + // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp + // These formats are already compressed, or don't compress well: + ".jpg", ".jpeg", ".png", ".gif", + ".wav", ".mp2", ".mp3", ".ogg", ".aac", + ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", + ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", + ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", + ".amr", ".awb", ".wma", ".wmv", + // Godot-specific: + ".webp", // Same reasoning as .png + ".cfb", // Don't let small config files slow-down startup + ".scn", // Binary scenes are usually already compressed + ".stex", // Streamable textures are usually already compressed + // Trailer for easier processing + nullptr + }; + + for (const char **ext = unconditional_compress_ext; *ext; ++ext) { + if (p_path.to_lower().ends_with(String(*ext))) { + return false; + } + } + + // -- Compressed resource? + + if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { + // Already compressed + return false; + } + + // --- TODO: Decide on texture resources according to their image compression setting + + return true; + } + + static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + AppxPackager *packager = (AppxPackager *)p_userdata; + String dst_path = p_path.replace_first("res://", "game/"); + + return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); + } + +public: + virtual String get_name() const override; + virtual String get_os_name() const override; + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + + virtual Ref<Texture2D> get_logo() const override; + + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + + virtual void get_export_options(List<ExportOption> *r_options) override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual void get_platform_features(List<String> *r_features) override; + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override; + + EditorExportPlatformUWP(); +}; + +#endif diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 10f953f2ec..4ff42f3f62 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -28,316 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/file_access.h" -#include "core/os/os.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "platform/windows/logo.gen.h" +#include "export.h" -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); - -class EditorExportPlatformWindows : public EditorExportPlatformPC { - void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); - -public: - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); - virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); - virtual void get_export_options(List<ExportOption> *r_options); -}; - -Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { - if (p_preset->get("codesign/enable")) { - return _code_sign(p_preset, p_path); - } else { - return OK; - } -} - -Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); - - if (err != OK) { - return err; - } - - _rcedit_add_data(p_preset, p_path); - - if (p_preset->get("codesign/enable") && err == OK) { - err = _code_sign(p_preset, p_path); - } - - return err; -} - -void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { - EditorExportPlatformPC::get_export_options(r_options); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); -#ifdef WINDOWS_ENABLED - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); -#endif - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); -} - -void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); - - if (rcedit_path == String()) { - return; - } - - if (!FileAccess::exists(rcedit_path)) { - ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included."); - return; - } - -#ifndef WINDOWS_ENABLED - // On non-Windows we need WINE to run rcedit - String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); - - if (wine_path != String() && !FileAccess::exists(wine_path)) { - ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); - return; - } - - if (wine_path == String()) { - wine_path = "wine"; // try to run wine from PATH - } -#endif - - String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon")); - String file_verion = p_preset->get("application/file_version"); - String product_version = p_preset->get("application/product_version"); - String company_name = p_preset->get("application/company_name"); - String product_name = p_preset->get("application/product_name"); - String file_description = p_preset->get("application/file_description"); - String copyright = p_preset->get("application/copyright"); - String trademarks = p_preset->get("application/trademarks"); - String comments = p_preset->get("application/comments"); - - List<String> args; - args.push_back(p_path); - if (icon_path != String()) { - args.push_back("--set-icon"); - args.push_back(icon_path); - } - if (file_verion != String()) { - args.push_back("--set-file-version"); - args.push_back(file_verion); - } - if (product_version != String()) { - args.push_back("--set-product-version"); - args.push_back(product_version); - } - if (company_name != String()) { - args.push_back("--set-version-string"); - args.push_back("CompanyName"); - args.push_back(company_name); - } - if (product_name != String()) { - args.push_back("--set-version-string"); - args.push_back("ProductName"); - args.push_back(product_name); - } - if (file_description != String()) { - args.push_back("--set-version-string"); - args.push_back("FileDescription"); - args.push_back(file_description); - } - if (copyright != String()) { - args.push_back("--set-version-string"); - args.push_back("LegalCopyright"); - args.push_back(copyright); - } - if (trademarks != String()) { - args.push_back("--set-version-string"); - args.push_back("LegalTrademarks"); - args.push_back(trademarks); - } - -#ifdef WINDOWS_ENABLED - OS::get_singleton()->execute(rcedit_path, args); -#else - // On non-Windows we need WINE to run rcedit - args.push_front(rcedit_path); - OS::get_singleton()->execute(wine_path, args); -#endif -} - -Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - List<String> args; - -#ifdef WINDOWS_ENABLED - String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); - if (signtool_path != String() && !FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - if (signtool_path == String()) { - signtool_path = "signtool"; // try to run signtool from PATH - } -#else - String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); - if (signtool_path != String() && !FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - if (signtool_path == String()) { - signtool_path = "osslsigncode"; // try to run signtool from PATH - } -#endif - - args.push_back("sign"); - - //identity -#ifdef WINDOWS_ENABLED - int id_type = p_preset->get("codesign/identity_type"); - if (id_type == 0) { //auto select - args.push_back("/a"); - } else if (id_type == 1) { //pkcs12 - if (p_preset->get("codesign/identity") != "") { - args.push_back("/f"); - args.push_back(p_preset->get("codesign/identity")); - } else { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } - } else if (id_type == 2) { //Windows certificate store - if (p_preset->get("codesign/identity") != "") { - args.push_back("/sha1"); - args.push_back(p_preset->get("codesign/identity")); - } else { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } - } else { - EditorNode::add_io_error("codesign: invalid identity type"); - return FAILED; - } -#else - if (p_preset->get("codesign/identity") != "") { - args.push_back("-pkcs12"); - args.push_back(p_preset->get("codesign/identity")); - } else { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } -#endif - - //password - if (p_preset->get("codesign/password") != "") { -#ifdef WINDOWS_ENABLED - args.push_back("/p"); -#else - args.push_back("-pass"); -#endif - args.push_back(p_preset->get("codesign/password")); - } - - //timestamp - if (p_preset->get("codesign/timestamp")) { - if (p_preset->get("codesign/timestamp_server") != "") { -#ifdef WINDOWS_ENABLED - args.push_back("/tr"); - args.push_back(p_preset->get("codesign/timestamp_server_url")); - args.push_back("/td"); - if ((int)p_preset->get("codesign/digest_algorithm") == 0) { - args.push_back("sha1"); - } else { - args.push_back("sha256"); - } -#else - args.push_back("-ts"); - args.push_back(p_preset->get("codesign/timestamp_server_url")); -#endif - } else { - EditorNode::add_io_error("codesign: invalid timestamp server"); - return FAILED; - } - } - - //digest -#ifdef WINDOWS_ENABLED - args.push_back("/fd"); -#else - args.push_back("-h"); -#endif - if ((int)p_preset->get("codesign/digest_algorithm") == 0) { - args.push_back("sha1"); - } else { - args.push_back("sha256"); - } - - //description - if (p_preset->get("codesign/description") != "") { -#ifdef WINDOWS_ENABLED - args.push_back("/d"); -#else - args.push_back("-n"); -#endif - args.push_back(p_preset->get("codesign/description")); - } - - //user options - PackedStringArray user_args = p_preset->get("codesign/custom_options"); - for (int i = 0; i < user_args.size(); i++) { - String user_arg = user_args[i].strip_edges(); - if (!user_arg.is_empty()) { - args.push_back(user_arg); - } - } - -#ifndef WINDOWS_ENABLED - args.push_back("-in"); -#endif - args.push_back(p_path); -#ifndef WINDOWS_ENABLED - args.push_back("-out"); - args.push_back(p_path + "_signed"); -#endif - - String str; - Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("codesign (" + p_path + "): " + str); -#ifndef WINDOWS_ENABLED - if (str.find("SignTool Error") != -1) { -#else - if (str.find("Failed") != -1) { -#endif - return FAILED; - } +#include "export_plugin.h" -#ifndef WINDOWS_ENABLED - DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); - - err = tmp_dir->remove(p_path); - ERR_FAIL_COND_V(err != OK, err); - - err = tmp_dir->rename(p_path + "_signed", p_path); - ERR_FAIL_COND_V(err != OK, err); -#endif - - return OK; -} +static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); void register_windows_exporter() { EDITOR_DEF("export/windows/rcedit", ""); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index 6a7131c73f..110e1439e2 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -28,4 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef WINDOWS_EXPORT_H +#define WINDOWS_EXPORT_H + void register_windows_exporter(); + +#endif diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp new file mode 100644 index 0000000000..165e86c066 --- /dev/null +++ b/platform/windows/export/export_plugin.cpp @@ -0,0 +1,323 @@ +/*************************************************************************/ +/* export_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 "export_plugin.h" + +Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { + if (p_preset->get("codesign/enable")) { + return _code_sign(p_preset, p_path); + } else { + return OK; + } +} + +Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); + + if (err != OK) { + return err; + } + + _rcedit_add_data(p_preset, p_path); + + if (p_preset->get("codesign/enable") && err == OK) { + err = _code_sign(p_preset, p_path); + } + + return err; +} + +void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { + EditorExportPlatformPC::get_export_options(r_options); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); +#ifdef WINDOWS_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); +#endif + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); +} + +void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); + + if (rcedit_path == String()) { + return; + } + + if (!FileAccess::exists(rcedit_path)) { + ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included."); + return; + } + +#ifndef WINDOWS_ENABLED + // On non-Windows we need WINE to run rcedit + String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); + + if (wine_path != String() && !FileAccess::exists(wine_path)) { + ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); + return; + } + + if (wine_path == String()) { + wine_path = "wine"; // try to run wine from PATH + } +#endif + + String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon")); + String file_verion = p_preset->get("application/file_version"); + String product_version = p_preset->get("application/product_version"); + String company_name = p_preset->get("application/company_name"); + String product_name = p_preset->get("application/product_name"); + String file_description = p_preset->get("application/file_description"); + String copyright = p_preset->get("application/copyright"); + String trademarks = p_preset->get("application/trademarks"); + String comments = p_preset->get("application/comments"); + + List<String> args; + args.push_back(p_path); + if (icon_path != String()) { + args.push_back("--set-icon"); + args.push_back(icon_path); + } + if (file_verion != String()) { + args.push_back("--set-file-version"); + args.push_back(file_verion); + } + if (product_version != String()) { + args.push_back("--set-product-version"); + args.push_back(product_version); + } + if (company_name != String()) { + args.push_back("--set-version-string"); + args.push_back("CompanyName"); + args.push_back(company_name); + } + if (product_name != String()) { + args.push_back("--set-version-string"); + args.push_back("ProductName"); + args.push_back(product_name); + } + if (file_description != String()) { + args.push_back("--set-version-string"); + args.push_back("FileDescription"); + args.push_back(file_description); + } + if (copyright != String()) { + args.push_back("--set-version-string"); + args.push_back("LegalCopyright"); + args.push_back(copyright); + } + if (trademarks != String()) { + args.push_back("--set-version-string"); + args.push_back("LegalTrademarks"); + args.push_back(trademarks); + } + +#ifdef WINDOWS_ENABLED + OS::get_singleton()->execute(rcedit_path, args); +#else + // On non-Windows we need WINE to run rcedit + args.push_front(rcedit_path); + OS::get_singleton()->execute(wine_path, args); +#endif +} + +Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + List<String> args; + +#ifdef WINDOWS_ENABLED + String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); + if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + if (signtool_path == String()) { + signtool_path = "signtool"; // try to run signtool from PATH + } +#else + String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); + if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + if (signtool_path == String()) { + signtool_path = "osslsigncode"; // try to run signtool from PATH + } +#endif + + args.push_back("sign"); + + //identity +#ifdef WINDOWS_ENABLED + int id_type = p_preset->get("codesign/identity_type"); + if (id_type == 0) { //auto select + args.push_back("/a"); + } else if (id_type == 1) { //pkcs12 + if (p_preset->get("codesign/identity") != "") { + args.push_back("/f"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + } else if (id_type == 2) { //Windows certificate store + if (p_preset->get("codesign/identity") != "") { + args.push_back("/sha1"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + } else { + EditorNode::add_io_error("codesign: invalid identity type"); + return FAILED; + } +#else + if (p_preset->get("codesign/identity") != "") { + args.push_back("-pkcs12"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } +#endif + + //password + if (p_preset->get("codesign/password") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/p"); +#else + args.push_back("-pass"); +#endif + args.push_back(p_preset->get("codesign/password")); + } + + //timestamp + if (p_preset->get("codesign/timestamp")) { + if (p_preset->get("codesign/timestamp_server") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/tr"); + args.push_back(p_preset->get("codesign/timestamp_server_url")); + args.push_back("/td"); + if ((int)p_preset->get("codesign/digest_algorithm") == 0) { + args.push_back("sha1"); + } else { + args.push_back("sha256"); + } +#else + args.push_back("-ts"); + args.push_back(p_preset->get("codesign/timestamp_server_url")); +#endif + } else { + EditorNode::add_io_error("codesign: invalid timestamp server"); + return FAILED; + } + } + + //digest +#ifdef WINDOWS_ENABLED + args.push_back("/fd"); +#else + args.push_back("-h"); +#endif + if ((int)p_preset->get("codesign/digest_algorithm") == 0) { + args.push_back("sha1"); + } else { + args.push_back("sha256"); + } + + //description + if (p_preset->get("codesign/description") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/d"); +#else + args.push_back("-n"); +#endif + args.push_back(p_preset->get("codesign/description")); + } + + //user options + PackedStringArray user_args = p_preset->get("codesign/custom_options"); + for (int i = 0; i < user_args.size(); i++) { + String user_arg = user_args[i].strip_edges(); + if (!user_arg.is_empty()) { + args.push_back(user_arg); + } + } + +#ifndef WINDOWS_ENABLED + args.push_back("-in"); +#endif + args.push_back(p_path); +#ifndef WINDOWS_ENABLED + args.push_back("-out"); + args.push_back(p_path + "_signed"); +#endif + + String str; + Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("codesign (" + p_path + "): " + str); +#ifndef WINDOWS_ENABLED + if (str.find("SignTool Error") != -1) { +#else + if (str.find("Failed") != -1) { +#endif + return FAILED; + } + +#ifndef WINDOWS_ENABLED + DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); + + err = tmp_dir->remove(p_path); + ERR_FAIL_COND_V(err != OK, err); + + err = tmp_dir->rename(p_path + "_signed", p_path); + ERR_FAIL_COND_V(err != OK, err); +#endif + + return OK; +} diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h new file mode 100644 index 0000000000..11d3826410 --- /dev/null +++ b/platform/windows/export/export_plugin.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* export_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 WINDOWS_EXPORT_PLUGIN_H +#define WINDOWS_EXPORT_PLUGIN_H + +#include "core/io/file_access.h" +#include "core/os/os.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "platform/windows/logo.gen.h" + +class EditorExportPlatformWindows : public EditorExportPlatformPC { + void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + +public: + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); + virtual void get_export_options(List<ExportOption> *r_options); +}; + +#endif diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 3ba3a4eec5..60f29ca163 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -147,36 +147,40 @@ uint32_t CollisionObject2D::get_collision_mask() const { return collision_mask; } -void CollisionObject2D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); +void CollisionObject2D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t collision_layer = get_collision_layer(); if (p_value) { - collision_layer |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - collision_layer &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } set_collision_layer(collision_layer); } -bool CollisionObject2D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CollisionObject2D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CollisionObject2D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void CollisionObject2D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool CollisionObject2D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CollisionObject2D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void CollisionObject2D::set_disable_mode(DisableMode p_mode) { @@ -565,10 +569,10 @@ void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject2D::get_collision_layer); ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject2D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject2D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CollisionObject2D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject2D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject2D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject2D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CollisionObject2D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject2D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject2D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject2D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject2D::set_disable_mode); ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject2D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index eca53eecfc..11e11d1382 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -107,11 +107,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index c478f03356..a8a2639ccf 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -62,14 +62,6 @@ static SegmentIntersectionResult segment_intersection( return SEGMENT_PARALLEL; } -// TODO I'm pretty sure there is an even faster way to swap things -template <typename T> -static inline void swap(T &a, T &b) { - T tmp = a; - a = b; - b = tmp; -} - static float calculate_total_distance(const Vector<Vector2> &points) { float d = 0.f; for (int i = 1; i < points.size(); ++i) { diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 6fd383ddab..a744ef40f6 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -170,10 +170,10 @@ void Node2D::set_scale(const Size2 &p_scale) { } _scale = p_scale; // Avoid having 0 scale values, can lead to errors in physics and rendering. - if (_scale.x == 0) { + if (Math::is_zero_approx(_scale.x)) { _scale.x = CMP_EPSILON; } - if (_scale.y == 0) { + if (Math::is_zero_approx(_scale.y)) { _scale.y = CMP_EPSILON; } _update_transform(); diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 602a0f2115..546536f546 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -54,20 +54,22 @@ uint32_t RayCast2D::get_collision_mask() const { return collision_mask; } -void RayCast2D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void RayCast2D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool RayCast2D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool RayCast2D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool RayCast2D::is_colliding() const { @@ -323,8 +325,8 @@ void RayCast2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &RayCast2D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &RayCast2D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &RayCast2D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &RayCast2D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &RayCast2D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &RayCast2D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast2D::set_exclude_parent_body); ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast2D::get_exclude_parent_body); diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index 984c6bda49..65b6e7899b 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -74,8 +74,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_exclude_parent_body(bool p_exclude_parent_body); bool get_exclude_parent_body() const; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index e504277a55..9dc1df202c 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -499,8 +499,8 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); - ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera3D::set_cull_mask_bit); - ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera3D::get_cull_mask_bit); + ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value); + ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value); //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); @@ -592,18 +592,22 @@ uint32_t Camera3D::get_cull_mask() const { return layers; } -void Camera3D::set_cull_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_cull_mask(layers | (1 << p_layer)); +void Camera3D::set_cull_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_cull_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_cull_mask(layers & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_cull_mask(mask); } -bool Camera3D::get_cull_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (layers & (1 << p_layer)); +bool Camera3D::get_cull_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return layers & (1 << (p_layer_number - 1)); } Vector<Plane> Camera3D::get_frustum() const { @@ -767,20 +771,22 @@ uint32_t ClippedCamera3D::get_collision_mask() const { return collision_mask; } -void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); +void ClippedCamera3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool ClippedCamera3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool ClippedCamera3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void ClippedCamera3D::add_exception_rid(const RID &p_rid) { @@ -843,8 +849,8 @@ void ClippedCamera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ClippedCamera3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ClippedCamera3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid); ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 61e3f51d0b..b7bf5566e7 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -147,8 +147,8 @@ public: void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; - void set_cull_mask_bit(int p_layer, bool p_enable); - bool get_cull_mask_bit(int p_layer) const; + void set_cull_mask_value(int p_layer_number, bool p_enable); + bool get_cull_mask_value(int p_layer_number) const; virtual Vector<Plane> get_frustum() const; bool is_position_in_frustum(const Vector3 &p_position) const; @@ -224,8 +224,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void add_exception_rid(const RID &p_rid); void add_exception(const Object *p_object); diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index dd1f25da68..75bb2995d3 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -146,36 +146,40 @@ uint32_t CollisionObject3D::get_collision_mask() const { return collision_mask; } -void CollisionObject3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); +void CollisionObject3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t collision_layer = get_collision_layer(); if (p_value) { - collision_layer |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - collision_layer &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } set_collision_layer(collision_layer); } -bool CollisionObject3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CollisionObject3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CollisionObject3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void CollisionObject3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool CollisionObject3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CollisionObject3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void CollisionObject3D::set_disable_mode(DisableMode p_mode) { @@ -423,10 +427,10 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject3D::get_collision_layer); ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CollisionObject3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject3D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CollisionObject3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject3D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject3D::set_disable_mode); ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject3D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &CollisionObject3D::set_ray_pickable); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 7c30a5cd98..2e74d84465 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -119,11 +119,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 2377c618aa..5a358e1917 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -710,15 +710,28 @@ void CPUParticles3D::_particles_process(double p_delta) { p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); } else { //initiate velocity spread in 3D - real_t angle1_rad = Math::atan2(direction.x, direction.z) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); - real_t angle2_rad = Math::atan2(direction.y, Math::abs(direction.z)) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * spread); + real_t angle1_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread); + real_t angle2_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread); Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); - direction_yz.z = direction_yz.z / MAX(0.0001, Math::sqrt(ABS(direction_yz.z))); //better uniform distribution - Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); - direction.normalize(); - p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + Vector3 spread_direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); + Vector3 direction_nrm = direction; + if (direction_nrm.length_squared() > 0) { + direction_nrm.normalize(); + } else { + direction_nrm = Vector3(0, 0, 1); + } + // rotate spread to direction + Vector3 binormal = Vector3(0.0, 1.0, 0.0).cross(direction_nrm); + if (binormal.length_squared() < 0.00000001) { + // direction is parallel to Y. Choose Z as the binormal. + binormal = Vector3(0.0, 0.0, 1.0); + } + binormal.normalize(); + Vector3 normal = binormal.cross(direction_nrm); + spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z; + p.velocity = spread_direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); } real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 3d1a27911b..f3e174c01b 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -195,18 +195,22 @@ uint32_t OccluderInstance3D::get_bake_mask() const { return bake_mask; } -void OccluderInstance3D::set_bake_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_bake_mask(bake_mask | (1 << p_layer)); +void OccluderInstance3D::set_bake_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_bake_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_bake_mask(bake_mask & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_bake_mask(mask); } -bool OccluderInstance3D::get_bake_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (bake_mask & (1 << p_layer)); +bool OccluderInstance3D::get_bake_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return bake_mask & (1 << (p_layer_number - 1)); } bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) { @@ -345,8 +349,8 @@ TypedArray<String> OccluderInstance3D::get_configuration_warnings() const { void OccluderInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask); ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask); - ClassDB::bind_method(D_METHOD("set_bake_mask_bit", "layer", "enabled"), &OccluderInstance3D::set_bake_mask_bit); - ClassDB::bind_method(D_METHOD("get_bake_mask_bit", "layer"), &OccluderInstance3D::get_bake_mask_bit); + ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &OccluderInstance3D::set_bake_mask_value); + ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &OccluderInstance3D::get_bake_mask_value); ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder); ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder); diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index d382cd090e..173614b80c 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -99,8 +99,9 @@ public: void set_bake_mask(uint32_t p_mask); uint32_t get_bake_mask() const; - void set_bake_mask_bit(int p_layer, bool p_enable); - bool get_bake_mask_bit(int p_layer) const; + void set_bake_mask_value(int p_layer_number, bool p_enable); + bool get_bake_mask_value(int p_layer_number) const; + BakeError bake(Node *p_from_node, String p_occluder_path = ""); OccluderInstance3D(); diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 7356ce478b..e757d6d3f4 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -60,20 +60,22 @@ uint32_t RayCast3D::get_collision_mask() const { return collision_mask; } -void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void RayCast3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool RayCast3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool RayCast3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool RayCast3D::is_colliding() const { @@ -303,8 +305,8 @@ void RayCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &RayCast3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &RayCast3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &RayCast3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &RayCast3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &RayCast3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &RayCast3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast3D::set_exclude_parent_body); ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast3D::get_exclude_parent_body); diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index 968cede9f2..3828bfb4c4 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -86,8 +86,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_exclude_parent_body(bool p_exclude_parent_body); bool get_exclude_parent_body() const; diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index a7ff0842d2..3dbeac5e97 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -327,11 +327,11 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody3D::set_collision_layer); ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody3D::get_collision_layer); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftBody3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftBody3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftBody3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftBody3D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore); ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore); @@ -515,36 +515,40 @@ uint32_t SoftBody3D::get_collision_layer() const { return collision_layer; } -void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool SoftBody3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool SoftBody3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool SoftBody3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool SoftBody3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void SoftBody3D::set_disable_mode(DisableMode p_mode) { diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index 81aa0c10c6..46b185a32c 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -138,11 +138,11 @@ public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index c155819159..b437379b2a 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -93,18 +93,22 @@ uint32_t VisualInstance3D::get_layer_mask() const { return layers; } -void VisualInstance3D::set_layer_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_layer_mask(layers | (1 << p_layer)); +void VisualInstance3D::set_layer_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_layer_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_layer_mask(layers & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_layer_mask(mask); } -bool VisualInstance3D::get_layer_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (layers & (1 << p_layer)); +bool VisualInstance3D::get_layer_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return layers & (1 << (p_layer_number - 1)); } void VisualInstance3D::_bind_methods() { @@ -114,8 +118,8 @@ void VisualInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance3D::get_instance); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance3D::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance3D::get_layer_mask); - ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance3D::set_layer_mask_bit); - ClassDB::bind_method(D_METHOD("get_layer_mask_bit", "layer"), &VisualInstance3D::get_layer_mask_bit); + ClassDB::bind_method(D_METHOD("set_layer_mask_value", "layer_number", "value"), &VisualInstance3D::set_layer_mask_value); + ClassDB::bind_method(D_METHOD("get_layer_mask_value", "layer_number"), &VisualInstance3D::get_layer_mask_value); ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance3D::get_transformed_aabb); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 97aac149a1..aa64195c2b 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -72,8 +72,8 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; - void set_layer_mask_bit(int p_layer, bool p_enable); - bool get_layer_mask_bit(int p_layer) const; + void set_layer_mask_value(int p_layer_number, bool p_enable); + bool get_layer_mask_value(int p_layer_number) const; VisualInstance3D(); ~VisualInstance3D(); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 32922f609d..87b950746c 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -46,9 +46,16 @@ void CodeEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { - set_gutter_width(main_gutter, get_row_height()); - set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width); - set_gutter_width(fold_gutter, get_row_height() / 1.2); + style_normal = get_theme_stylebox(SNAME("normal")); + + font = get_theme_font(SNAME("font")); + font_size = get_theme_font_size(SNAME("font_size")); + + line_spacing = get_theme_constant(SNAME("line_spacing")); + + set_gutter_width(main_gutter, get_line_height()); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', 0, font_size).width); + set_gutter_width(fold_gutter, get_line_height() / 1.2); breakpoint_color = get_theme_color(SNAME("breakpoint_color")); breakpoint_icon = get_theme_icon(SNAME("breakpoint")); @@ -65,7 +72,7 @@ void CodeEdit::_notification(int p_what) { can_fold_icon = get_theme_icon(SNAME("can_fold")); folded_icon = get_theme_icon(SNAME("folded")); - code_completion_max_width = get_theme_constant(SNAME("completion_max_width")) * cache.font->get_char_size('x').x; + code_completion_max_width = get_theme_constant(SNAME("completion_max_width")) * font->get_char_size('x').x; code_completion_max_lines = get_theme_constant(SNAME("completion_lines")); code_completion_scroll_width = get_theme_constant(SNAME("completion_scroll_width")); code_completion_scroll_color = get_theme_color(SNAME("completion_scroll_color")); @@ -80,12 +87,12 @@ void CodeEdit::_notification(int p_what) { const Size2 size = get_size(); const bool caret_visible = is_caret_visible(); const bool rtl = is_layout_rtl(); - const int row_height = get_row_height(); + const int row_height = get_line_height(); if (line_length_guideline_columns.size() > 0) { - const int xmargin_beg = cache.style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); - const int xmargin_end = size.width - cache.style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); - const int char_size = (int)cache.font->get_char_size('0', 0, cache.font_size).width; + const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); + const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); + const int char_size = (int)font->get_char_size('0', 0, font_size).width; for (int i = 0; i < line_length_guideline_columns.size(); i++) { const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll(); @@ -115,14 +122,14 @@ void CodeEdit::_notification(int p_what) { const Point2 caret_pos = get_caret_draw_pos(); const int total_height = csb->get_minimum_size().y + code_completion_rect.size.height; if (caret_pos.y + row_height + total_height > get_size().height) { - code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + cache.line_spacing; + code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + line_spacing; } else { - code_completion_rect.position.y = caret_pos.y + (cache.line_spacing / 2.0f); + code_completion_rect.position.y = caret_pos.y + (line_spacing / 2.0f); code_completion_below = true; } const int scroll_width = code_completion_options_count > code_completion_max_lines ? code_completion_scroll_width : 0; - const int code_completion_base_width = cache.font->get_string_size(code_completion_base).width; + const int code_completion_base_width = font->get_string_size(code_completion_base).width; if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) { code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width; } else { @@ -144,7 +151,7 @@ void CodeEdit::_notification(int p_what) { Ref<TextLine> tl; tl.instantiate(); - tl->add_string(code_completion_options[l].display, cache.font, cache.font_size); + tl->add_string(code_completion_options[l].display, font, font_size); int yofs = (row_height - tl->get_size().y) / 2; Point2 title_pos(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height + yofs); @@ -183,8 +190,7 @@ void CodeEdit::_notification(int p_what) { /* Code hint */ if (caret_visible && code_hint != "" && (!code_completion_active || (code_completion_below != code_hint_draw_below))) { - const Ref<Font> font = cache.font; - const int font_height = font->get_height(cache.font_size); + const int font_height = font->get_height(font_size); Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")); Color font_color = get_theme_color(SNAME("font_color"), SNAME("TooltipLabel")); @@ -193,37 +199,37 @@ void CodeEdit::_notification(int p_what) { int max_width = 0; for (int i = 0; i < line_count; i++) { - max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], cache.font_size).x); + max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], font_size).x); } - Size2 minsize = sb->get_minimum_size() + Size2(max_width, line_count * font_height + (cache.line_spacing * line_count - 1)); + Size2 minsize = sb->get_minimum_size() + Size2(max_width, line_count * font_height + (line_spacing * line_count - 1)); - int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), cache.font_size).x; + int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), font_size).x; if (code_hint_xpos == -0xFFFF) { code_hint_xpos = get_caret_draw_pos().x - offset; } Point2 hint_ofs = Vector2(code_hint_xpos, get_caret_draw_pos().y); if (code_hint_draw_below) { - hint_ofs.y += cache.line_spacing / 2.0f; + hint_ofs.y += line_spacing / 2.0f; } else { - hint_ofs.y -= (minsize.y + row_height) - cache.line_spacing; + hint_ofs.y -= (minsize.y + row_height) - line_spacing; } draw_style_box(sb, Rect2(hint_ofs, minsize)); - int line_spacing = 0; + int yofs = 0; for (int i = 0; i < line_count; i++) { const String &line = code_hint_lines[i]; int begin = 0; int end = 0; if (line.find(String::chr(0xFFFF)) != -1) { - begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), cache.font_size).x; - end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), cache.font_size).x; + begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), font_size).x; + end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x; } - Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font_height * i + line_spacing); + Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font_height * i + yofs); round_ofs = round_ofs.round(); - draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, cache.font_size, font_color); + draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, font_size, font_color); if (end > 0) { // Draw an underline for the currently edited function parameter. const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing); @@ -235,7 +241,7 @@ void CodeEdit::_notification(int p_what) { Vector2(end - begin, font_height)); draw_rect(highlight_rect, font_color * Color(1, 1, 1, 0.2)); } - line_spacing += cache.line_spacing; + yofs += line_spacing; } } } break; @@ -270,7 +276,7 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } break; case MOUSE_BUTTON_LEFT: { - code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_row_height(), 0, code_completion_options.size() - 1); + code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_line_height(), 0, code_completion_options.size() - 1); if (mb->is_double_click()) { confirm_code_completion(); } @@ -290,14 +296,15 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } - int line, col; - _get_mouse_pos(Point2i(mpos.x, mpos.y), line, col); + Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + int line = pos.y; + int col = pos.x; if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (is_line_folded(line)) { - int wrap_index = get_line_wrap_index_at_col(line, col); - if (wrap_index == times_line_wraps(line)) { - int eol_icon_width = cache.folded_eol_icon->get_width(); + int wrap_index = get_line_wrap_index_at_column(line, col); + if (wrap_index == get_line_wrap_count(line)) { + int eol_icon_width = folded_eol_icon->get_width(); int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll(); if (mpos.x > left_margin && mpos.x <= left_margin + eol_icon_width + 3) { unfold_line(line); @@ -313,8 +320,10 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (is_layout_rtl()) { mpos.x = get_size().x - mpos.x; } - int line, col; - _get_mouse_pos(Point2i(mpos.x, mpos.y), line, col); + + Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + int line = pos.y; + int col = pos.x; emit_signal(SNAME("symbol_lookup"), symbol_lookup_word, line, col); return; @@ -357,7 +366,7 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { #endif if (symbol_lookup_on_click_enabled) { if (k->is_pressed() && !is_dragging_cursor()) { - symbol_lookup_new_word = get_word_at_pos(_get_local_mouse_pos()); + symbol_lookup_new_word = get_word_at_pos(get_local_mouse_pos()); if (symbol_lookup_new_word != symbol_lookup_word) { emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word); } @@ -523,17 +532,18 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { return CURSOR_POINTING_HAND; } - if ((code_completion_active && code_completion_rect.has_point(p_pos)) || (is_readonly() && (!is_selecting_enabled() || get_line_count() == 0))) { + if ((code_completion_active && code_completion_rect.has_point(p_pos)) || (!is_editable() && (!is_selecting_enabled() || get_line_count() == 0))) { return CURSOR_ARROW; } - int line, col; - _get_mouse_pos(p_pos, line, col); + Point2i pos = get_line_column_at_pos(p_pos); + int line = pos.y; + int col = pos.x; if (is_line_folded(line)) { - int wrap_index = get_line_wrap_index_at_col(line, col); - if (wrap_index == times_line_wraps(line)) { - int eol_icon_width = cache.folded_eol_icon->get_width(); + int wrap_index = get_line_wrap_index_at_column(line, col); + if (wrap_index == get_line_wrap_count(line)) { + int eol_icon_width = folded_eol_icon->get_width(); int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll(); if (p_pos.x > left_margin && p_pos.x <= left_margin + eol_icon_width + 3) { return CURSOR_POINTING_HAND; @@ -544,58 +554,118 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { return TextEdit::get_cursor_shape(p_pos); } -void CodeEdit::handle_unicode_input(uint32_t p_unicode) { - bool had_selection = is_selection_active(); +/* Text manipulation */ + +// Overridable actions +void CodeEdit::_handle_unicode_input(const uint32_t p_unicode) { + bool had_selection = has_selection(); if (had_selection) { begin_complex_operation(); delete_selection(); } - // Remove the old character if in insert mode and no selection. - if (is_insert_mode() && !had_selection) { + // Remove the old character if in overtype mode and no selection. + if (is_overtype_mode_enabled() && !had_selection) { begin_complex_operation(); - // Make sure we don't try and remove empty space. - if (cursor_get_column() < get_line(cursor_get_line()).length()) { - _remove_text(cursor_get_line(), cursor_get_column(), cursor_get_line(), cursor_get_column() + 1); + /* Make sure we don't try and remove empty space. */ + if (get_caret_column() < get_line(get_caret_line()).length()) { + remove_text(get_caret_line(), get_caret_column(), get_caret_line(), get_caret_column() + 1); } } const char32_t chr[2] = { (char32_t)p_unicode, 0 }; if (auto_brace_completion_enabled) { - int cl = cursor_get_line(); - int cc = cursor_get_column(); + int cl = get_caret_line(); + int cc = get_caret_column(); int caret_move_offset = 1; int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1; if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) { - insert_text_at_cursor(chr); + insert_text_at_caret(chr); } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) { - insert_text_at_cursor(chr); + insert_text_at_caret(chr); } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) { caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length(); } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) { - insert_text_at_cursor(chr); + insert_text_at_caret(chr); } else { - insert_text_at_cursor(chr); + insert_text_at_caret(chr); int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1); if (pre_brace_pair != -1) { - insert_text_at_cursor(auto_brace_completion_pairs[pre_brace_pair].close_key); + insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key); } } - cursor_set_column(cc + caret_move_offset); + set_caret_column(cc + caret_move_offset); } else { - insert_text_at_cursor(chr); + insert_text_at_caret(chr); } - if ((is_insert_mode() && !had_selection) || (had_selection)) { + if ((is_overtype_mode_enabled() && !had_selection) || (had_selection)) { end_complex_operation(); } } +void CodeEdit::_backspace() { + if (!is_editable()) { + return; + } + + int cc = get_caret_column(); + int cl = get_caret_line(); + + if (cc == 0 && cl == 0) { + return; + } + + if (has_selection()) { + delete_selection(); + return; + } + + if (cl > 0 && _is_line_hidden(cl - 1)) { + unfold_line(get_caret_line() - 1); + } + + int prev_line = cc ? cl : cl - 1; + int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length()); + + merge_gutters(cl, prev_line); + + if (auto_brace_completion_enabled && cc > 0) { + int idx = _get_auto_brace_pair_open_at_pos(cl, cc); + if (idx != -1) { + prev_column = cc - auto_brace_completion_pairs[idx].open_key.length(); + + if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) { + remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length()); + } else { + remove_text(prev_line, prev_column, cl, cc); + } + set_caret_line(prev_line, false, true); + set_caret_column(prev_column); + return; + } + } + + // For space indentation we need to do a simple unindent if there are no chars to the left, acting in the + // same way as tabs. + if (indent_using_spaces && cc != 0) { + if (get_first_non_whitespace_column(cl) > cc) { + prev_column = cc - _calculate_spaces_till_next_left_indent(cc); + prev_line = cl; + } + } + + remove_text(prev_line, prev_column, cl, cc); + + set_caret_line(prev_line, false, true); + set_caret_column(prev_column); +} + /* Indent management */ void CodeEdit::set_indent_size(const int p_size) { ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0."); @@ -654,28 +724,28 @@ TypedArray<String> CodeEdit::get_auto_indent_prefixes() const { } void CodeEdit::do_indent() { - if (is_readonly()) { + if (!is_editable()) { return; } - if (is_selection_active()) { + if (has_selection()) { indent_lines(); return; } if (!indent_using_spaces) { - insert_text_at_cursor("\t"); + insert_text_at_caret("\t"); return; } - int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor_get_column()); + int spaces_to_add = _calculate_spaces_till_next_right_indent(get_caret_column()); if (spaces_to_add > 0) { - insert_text_at_cursor(String(" ").repeat(spaces_to_add)); + insert_text_at_caret(String(" ").repeat(spaces_to_add)); } } void CodeEdit::indent_lines() { - if (is_readonly()) { + if (!is_editable()) { return; } @@ -685,9 +755,9 @@ void CodeEdit::indent_lines() { /* Default is 1 for tab indentation. */ int selection_offset = 1; - int start_line = cursor_get_line(); + int start_line = get_caret_line(); int end_line = start_line; - if (is_selection_active()) { + if (has_selection()) { start_line = get_selection_from_line(); end_line = get_selection_to_line(); @@ -700,7 +770,7 @@ void CodeEdit::indent_lines() { for (int i = start_line; i <= end_line; i++) { const String line_text = get_line(i); - if (line_text.size() == 0 && is_selection_active()) { + if (line_text.size() == 0 && has_selection()) { continue; } @@ -717,32 +787,32 @@ void CodeEdit::indent_lines() { } /* Fix selection and caret being off after shifting selection right.*/ - if (is_selection_active()) { + if (has_selection()) { select(start_line, get_selection_from_column() + selection_offset, get_selection_to_line(), get_selection_to_column() + selection_offset); } - cursor_set_column(cursor_get_column() + selection_offset, false); + set_caret_column(get_caret_column() + selection_offset, false); end_complex_operation(); } void CodeEdit::do_unindent() { - if (is_readonly()) { + if (!is_editable()) { return; } - int cc = cursor_get_column(); + int cc = get_caret_column(); - if (is_selection_active() || cc <= 0) { + if (has_selection() || cc <= 0) { unindent_lines(); return; } - int cl = cursor_get_line(); + int cl = get_caret_line(); const String &line = get_line(cl); if (line[cc - 1] == '\t') { - _remove_text(cl, cc - 1, cl, cc); - cursor_set_column(MAX(0, cc - 1)); + remove_text(cl, cc - 1, cl, cc); + set_caret_column(MAX(0, cc - 1)); return; } @@ -758,13 +828,13 @@ void CodeEdit::do_unindent() { break; } } - _remove_text(cl, cc - spaces_to_remove, cl, cc); - cursor_set_column(MAX(0, cc - spaces_to_remove)); + remove_text(cl, cc - spaces_to_remove, cl, cc); + set_caret_column(MAX(0, cc - spaces_to_remove)); } } void CodeEdit::unindent_lines() { - if (is_readonly()) { + if (!is_editable()) { return; } @@ -775,11 +845,11 @@ void CodeEdit::unindent_lines() { /* therefore we just remember initial values and at the end of the operation offset them by number of removed characters. */ int removed_characters = 0; int initial_selection_end_column = 0; - int initial_cursor_column = cursor_get_column(); + int initial_cursor_column = get_caret_column(); - int start_line = cursor_get_line(); + int start_line = get_caret_line(); int end_line = start_line; - if (is_selection_active()) { + if (has_selection()) { start_line = get_selection_from_line(); end_line = get_selection_to_line(); @@ -822,7 +892,7 @@ void CodeEdit::unindent_lines() { } } - if (is_selection_active()) { + if (has_selection()) { /* Fix selection being off by one on the first line. */ if (first_line_edited) { select(get_selection_from_line(), get_selection_from_column() - removed_characters, get_selection_to_line(), initial_selection_end_column); @@ -833,7 +903,7 @@ void CodeEdit::unindent_lines() { select(get_selection_from_line(), get_selection_from_column(), get_selection_to_line(), initial_selection_end_column - removed_characters); } } - cursor_set_column(initial_cursor_column - removed_characters, false); + set_caret_column(initial_cursor_column - removed_characters, false); end_complex_operation(); } @@ -851,12 +921,12 @@ int CodeEdit::_calculate_spaces_till_next_right_indent(int p_column) const { } void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { - if (is_readonly()) { + if (!is_editable()) { return; } - const int cc = cursor_get_column(); - const int cl = cursor_get_line(); + const int cc = get_caret_column(); + const int cl = get_caret_line(); const String line = get_line(cl); String ins = "\n"; @@ -932,86 +1002,29 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { if (!p_split_current_line) { if (p_above) { if (cl > 0) { - cursor_set_line(cl - 1, false); - cursor_set_column(get_line(cursor_get_line()).length()); + set_caret_line(cl - 1, false); + set_caret_column(get_line(get_caret_line()).length()); } else { - cursor_set_column(0); + set_caret_column(0); first_line = true; } } else { - cursor_set_column(line.length()); + set_caret_column(line.length()); } } - insert_text_at_cursor(ins); + insert_text_at_caret(ins); if (first_line) { - cursor_set_line(0); + set_caret_line(0); } else if (brace_indent) { - cursor_set_line(cursor_get_line() - 1, false); - cursor_set_column(get_line(cursor_get_line()).length()); + set_caret_line(get_caret_line() - 1, false); + set_caret_column(get_line(get_caret_line()).length()); } end_complex_operation(); } -void CodeEdit::backspace() { - if (is_readonly()) { - return; - } - - int cc = cursor_get_column(); - int cl = cursor_get_line(); - - if (cc == 0 && cl == 0) { - return; - } - - if (is_selection_active()) { - delete_selection(); - return; - } - - if (cl > 0 && is_line_hidden(cl - 1)) { - unfold_line(cursor_get_line() - 1); - } - - int prev_line = cc ? cl : cl - 1; - int prev_column = cc ? (cc - 1) : (get_line(cl - 1).length()); - - merge_gutters(cl, prev_line); - - if (auto_brace_completion_enabled && cc > 0) { - int idx = _get_auto_brace_pair_open_at_pos(cl, cc); - if (idx != -1) { - prev_column = cc - auto_brace_completion_pairs[idx].open_key.length(); - - if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) { - _remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length()); - } else { - _remove_text(prev_line, prev_column, cl, cc); - } - cursor_set_line(prev_line, false, true); - cursor_set_column(prev_column); - return; - } - } - - /* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */ - /* same way as tabs. */ - if (indent_using_spaces && cc != 0) { - if (get_first_non_whitespace_column(cl) > cc) { - prev_column = cc - _calculate_spaces_till_next_left_indent(cc); - prev_line = cl; - } - } - - _remove_text(prev_line, prev_column, cl, cc); - - cursor_set_line(prev_line, false, true); - cursor_set_column(prev_column); -} - /* Auto brace completion */ void CodeEdit::set_auto_brace_completion_enabled(bool p_enabled) { auto_brace_completion_enabled = p_enabled; @@ -1278,8 +1291,8 @@ void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 String fc = TS->format_number(String::num(p_line + 1).lpad(line_number_digits, line_number_padding)); Ref<TextLine> tl; tl.instantiate(); - tl->add_string(fc, cache.font, cache.font_size); - int yofs = p_region.position.y + (get_row_height() - tl->get_size().y) / 2; + tl->add_string(fc, font, font_size); + int yofs = p_region.position.y + (get_line_height() - tl->get_size().y) / 2; Color number_color = get_line_gutter_item_color(p_line, line_number_gutter); if (number_color == Color(1, 1, 1)) { number_color = line_number_color; @@ -1319,7 +1332,7 @@ void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_regi /* Line Folding */ void CodeEdit::set_line_folding_enabled(bool p_enabled) { line_folding_enabled = p_enabled; - set_hiding_enabled(p_enabled); + _set_hiding_enabled(p_enabled); } bool CodeEdit::is_line_folding_enabled() const { @@ -1336,7 +1349,7 @@ bool CodeEdit::can_fold_line(int p_line) const { return false; } - if (is_line_hidden(p_line) || is_line_folded(p_line)) { + if (_is_line_hidden(p_line) || is_line_folded(p_line)) { return false; } @@ -1416,31 +1429,31 @@ void CodeEdit::fold_line(int p_line) { } for (int i = p_line + 1; i <= end_line; i++) { - set_line_as_hidden(i, true); + _set_line_as_hidden(i, true); } /* Fix selection. */ - if (is_selection_active()) { - if (is_line_hidden(get_selection_from_line()) && is_line_hidden(get_selection_to_line())) { + if (has_selection()) { + if (_is_line_hidden(get_selection_from_line()) && _is_line_hidden(get_selection_to_line())) { deselect(); - } else if (is_line_hidden(get_selection_from_line())) { + } else if (_is_line_hidden(get_selection_from_line())) { select(p_line, 9999, get_selection_to_line(), get_selection_to_column()); - } else if (is_line_hidden(get_selection_to_line())) { + } else if (_is_line_hidden(get_selection_to_line())) { select(get_selection_from_line(), get_selection_from_column(), p_line, 9999); } } /* Reset caret. */ - if (is_line_hidden(cursor_get_line())) { - cursor_set_line(p_line, false, false); - cursor_set_column(get_line(p_line).length(), false); + if (_is_line_hidden(get_caret_line())) { + set_caret_line(p_line, false, false); + set_caret_column(get_line(p_line).length(), false); } update(); } void CodeEdit::unfold_line(int p_line) { ERR_FAIL_INDEX(p_line, get_line_count()); - if (!is_line_folded(p_line) && !is_line_hidden(p_line)) { + if (!is_line_folded(p_line) && !_is_line_hidden(p_line)) { return; } @@ -1453,10 +1466,10 @@ void CodeEdit::unfold_line(int p_line) { fold_start = is_line_folded(fold_start) ? fold_start : p_line; for (int i = fold_start + 1; i < get_line_count(); i++) { - if (!is_line_hidden(i)) { + if (!_is_line_hidden(i)) { break; } - set_line_as_hidden(i, false); + _set_line_as_hidden(i, false); } update(); } @@ -1469,7 +1482,7 @@ void CodeEdit::fold_all_lines() { } void CodeEdit::unfold_all_lines() { - unhide_all_lines(); + _unhide_all_lines(); } void CodeEdit::toggle_foldable_line(int p_line) { @@ -1483,7 +1496,7 @@ void CodeEdit::toggle_foldable_line(int p_line) { bool CodeEdit::is_line_folded(int p_line) const { ERR_FAIL_INDEX_V(p_line, get_line_count(), false); - return p_line + 1 < get_line_count() && !is_line_hidden(p_line) && is_line_hidden(p_line + 1); + return p_line + 1 < get_line_count() && !_is_line_hidden(p_line) && _is_line_hidden(p_line + 1); } TypedArray<int> CodeEdit::get_folded_lines() const { @@ -1707,11 +1720,11 @@ String CodeEdit::get_text_for_code_completion() const { for (int i = 0; i < text_size; i++) { String line = get_line(i); - if (i == cursor_get_line()) { - completion_text += line.substr(0, cursor_get_column()); + if (i == get_caret_line()) { + completion_text += line.substr(0, get_caret_column()); /* Not unicode, represents the caret. */ completion_text += String::chr(0xFFFF); - completion_text += line.substr(cursor_get_column(), line.size()); + completion_text += line.substr(get_caret_column(), line.size()); } else { completion_text += line; } @@ -1758,10 +1771,10 @@ void CodeEdit::request_code_completion(bool p_force) { return; } - String line = get_line(cursor_get_line()); - int ofs = CLAMP(cursor_get_column(), 0, line.length()); + String line = get_line(get_caret_line()); + int ofs = CLAMP(get_caret_column(), 0, line.length()); - if (ofs > 0 && (is_in_string(cursor_get_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(String::chr(line[ofs - 1])))) { + if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(String::chr(line[ofs - 1])))) { emit_signal(SNAME("request_code_completion")); } else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(String::chr(line[ofs - 2]))) { emit_signal(SNAME("request_code_completion")); @@ -1836,7 +1849,7 @@ void CodeEdit::set_code_completion_selected_index(int p_index) { } void CodeEdit::confirm_code_completion(bool p_replace) { - if (is_readonly() || !code_completion_active) { + if (!is_editable() || !code_completion_active) { return; } @@ -1847,7 +1860,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) { } begin_complex_operation(); - int caret_line = cursor_get_line(); + int caret_line = get_caret_line(); const String &insert_text = code_completion_options[code_completion_current_selected].insert_text; const String &display_text = code_completion_options[code_completion_current_selected].display; @@ -1855,7 +1868,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) { if (p_replace) { /* Find end of current section */ const String line = get_line(caret_line); - int caret_col = cursor_get_column(); + int caret_col = get_caret_column(); int caret_remove_line = caret_line; bool merge_text = true; @@ -1878,13 +1891,13 @@ void CodeEdit::confirm_code_completion(bool p_replace) { } /* Replace. */ - _remove_text(caret_line, cursor_get_column() - code_completion_base.length(), caret_remove_line, caret_col); - cursor_set_column(cursor_get_column() - code_completion_base.length(), false); - insert_text_at_cursor(insert_text); + remove_text(caret_line, get_caret_column() - code_completion_base.length(), caret_remove_line, caret_col); + set_caret_column(get_caret_column() - code_completion_base.length(), false); + insert_text_at_caret(insert_text); } else { /* Get first non-matching char. */ const String line = get_line(caret_line); - int caret_col = cursor_get_column(); + int caret_col = get_caret_column(); int matching_chars = code_completion_base.length(); for (; matching_chars <= insert_text.length(); matching_chars++) { if (caret_col >= line.length() || line[caret_col] != insert_text[matching_chars]) { @@ -1894,41 +1907,41 @@ void CodeEdit::confirm_code_completion(bool p_replace) { } /* Remove base completion text. */ - _remove_text(caret_line, cursor_get_column() - code_completion_base.length(), caret_line, cursor_get_column()); - cursor_set_column(cursor_get_column() - code_completion_base.length(), false); + remove_text(caret_line, get_caret_column() - code_completion_base.length(), caret_line, get_caret_column()); + set_caret_column(get_caret_column() - code_completion_base.length(), false); /* Merge with text. */ - insert_text_at_cursor(insert_text.substr(0, code_completion_base.length())); - cursor_set_column(caret_col, false); - insert_text_at_cursor(insert_text.substr(matching_chars)); + insert_text_at_caret(insert_text.substr(0, code_completion_base.length())); + set_caret_column(caret_col, false); + insert_text_at_caret(insert_text.substr(matching_chars)); } /* Handle merging of symbols eg strings, brackets. */ const String line = get_line(caret_line); - char32_t next_char = line[cursor_get_column()]; + char32_t next_char = line[get_caret_column()]; char32_t last_completion_char = insert_text[insert_text.length() - 1]; char32_t last_completion_char_display = display_text[display_text.length() - 1]; - int pre_brace_pair = cursor_get_column() > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, cursor_get_column()) : -1; - int post_brace_pair = cursor_get_column() < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column()) : -1; + int pre_brace_pair = get_caret_column() > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column()) : -1; + int post_brace_pair = get_caret_column() < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column()) : -1; if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) { - _remove_text(caret_line, cursor_get_column(), caret_line, cursor_get_column() + 1); + remove_text(caret_line, get_caret_column(), caret_line, get_caret_column() + 1); } if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) { - _remove_text(caret_line, cursor_get_column(), caret_line, cursor_get_column() + 1); + remove_text(caret_line, get_caret_column(), caret_line, get_caret_column() + 1); } else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) { - insert_text_at_cursor(auto_brace_completion_pairs[pre_brace_pair].close_key); - cursor_set_column(cursor_get_column() - auto_brace_completion_pairs[pre_brace_pair].close_key.length()); + insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key); + set_caret_column(get_caret_column() - auto_brace_completion_pairs[pre_brace_pair].close_key.length()); } - if (pre_brace_pair == -1 && post_brace_pair == -1 && cursor_get_column() > 0 && cursor_get_column() < get_line(caret_line).length()) { - pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, cursor_get_column() + 1); - if (pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column() - 1)) { - _remove_text(caret_line, cursor_get_column() - 2, caret_line, cursor_get_column()); - if (_get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column() - 1) != pre_brace_pair) { - cursor_set_column(cursor_get_column() - 1); + if (pre_brace_pair == -1 && post_brace_pair == -1 && get_caret_column() > 0 && get_caret_column() < get_line(caret_line).length()) { + pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, get_caret_column() + 1); + if (pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1)) { + remove_text(caret_line, get_caret_column() - 2, caret_line, get_caret_column()); + if (_get_auto_brace_pair_close_at_pos(caret_line, get_caret_column() - 1) != pre_brace_pair) { + set_caret_column(get_caret_column() - 1); } } } @@ -1971,9 +1984,11 @@ bool CodeEdit::is_symbol_lookup_on_click_enabled() const { } String CodeEdit::get_text_for_symbol_lookup() { - int line, col; - Point2i mp = _get_local_mouse_pos(); - _get_mouse_pos(mp, line, col); + Point2i mp = get_local_mouse_pos(); + + Point2i pos = get_line_column_at_pos(mp); + int line = pos.y; + int col = pos.x; StringBuilder lookup_text; const int text_size = get_line_count(); @@ -2289,8 +2304,8 @@ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) { if (p_gutter == line_number_gutter) { set_selection_mode(TextEdit::SelectionMode::SELECTION_MODE_LINE, p_line, 0); select(p_line, 0, p_line + 1, 0); - cursor_set_line(p_line + 1); - cursor_set_column(0); + set_caret_line(p_line + 1); + set_caret_column(0); return; } @@ -2674,7 +2689,7 @@ void CodeEdit::_filter_code_completion_candidates() { option.icon = completion_options[i].get("icon"); option.default_value = completion_options[i].get("default_value"); - max_width = MAX(max_width, cache.font->get_string_size(option.display).width); + max_width = MAX(max_width, font->get_string_size(option.display).width); code_completion_options.push_back(option); } @@ -2685,8 +2700,8 @@ void CodeEdit::_filter_code_completion_candidates() { return; } - const int caret_line = cursor_get_line(); - const int caret_column = cursor_get_column(); + const int caret_line = get_caret_line(); + const int caret_column = get_caret_column(); const String line = get_line(caret_line); if (caret_column > 0 && line[caret_column - 1] == '(' && !code_completion_forced) { @@ -2778,7 +2793,7 @@ void CodeEdit::_filter_code_completion_candidates() { if (string_to_complete.length() == 0) { code_completion_options.push_back(option); - max_width = MAX(max_width, cache.font->get_string_size(option.display).width); + max_width = MAX(max_width, font->get_string_size(option.display).width); continue; } @@ -2827,7 +2842,7 @@ void CodeEdit::_filter_code_completion_candidates() { } else { completion_options_subseq.push_back(option); } - max_width = MAX(max_width, cache.font->get_string_size(option.display).width); + max_width = MAX(max_width, font->get_string_size(option.display).width); /* Matched the whole subsequence in s_lower. */ } else if (!*ssq_lower) { /* Finished matching in the first s.length() characters. */ @@ -2836,7 +2851,7 @@ void CodeEdit::_filter_code_completion_candidates() { } else { completion_options_subseq_casei.push_back(option); } - max_width = MAX(max_width, cache.font->get_string_size(option.display).width); + max_width = MAX(max_width, font->get_string_size(option.display).width); } } @@ -2874,7 +2889,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { while (lc /= 10) { line_number_digits++; } - set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0', 0, cache.font_size).width); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', 0, font_size).width); int from_line = MIN(p_from_line, p_to_line); int line_count = (p_to_line - p_from_line); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 72fdc6e787..558c7adaea 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -233,16 +233,29 @@ private: String symbol_lookup_new_word = ""; String symbol_lookup_word = ""; + /* Visual */ + Ref<StyleBox> style_normal; + + Ref<Font> font; + int font_size = 16; + + int line_spacing = 1; + protected: void _gui_input(const Ref<InputEvent> &p_gui_input) override; void _notification(int p_what); static void _bind_methods(); + /* Text manipulation */ + + // Overridable actions + virtual void _handle_unicode_input(const uint32_t p_unicode) override; + virtual void _backspace() override; + public: /* General overrides */ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; - virtual void handle_unicode_input(uint32_t p_unicode) override; /* Indent management */ void set_indent_size(const int p_size); @@ -263,8 +276,6 @@ public: void indent_lines(); void unindent_lines(); - virtual void backspace() override; - /* Auto brace completion */ void set_auto_brace_completion_enabled(bool p_enabled); bool is_auto_brace_completion_enabled() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 985abf8e55..65f6cfcf17 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -45,12 +45,6 @@ #include "editor/editor_scale.h" #endif -#define TAB_PIXELS - -inline bool _is_symbol(char32_t c) { - return is_symbol(c); -} - static bool _is_text_char(char32_t c) { return !is_symbol(c); } @@ -64,6 +58,8 @@ static bool _is_char(char32_t c) { } /////////////////////////////////////////////////////////////////////////////// +/// TEXT /// +/////////////////////////////////////////////////////////////////////////////// void TextEdit::Text::set_font(const Ref<Font> &p_font) { font = p_font; @@ -85,7 +81,7 @@ void TextEdit::Text::set_font_features(const Dictionary &p_features) { opentype_features = p_features; } -void TextEdit::Text::set_direction_and_language(TextServer::Direction p_direction, String p_language) { +void TextEdit::Text::set_direction_and_language(TextServer::Direction p_direction, const String &p_language) { direction = p_direction; language = p_language; } @@ -249,257 +245,25 @@ void TextEdit::Text::move_gutters(int p_from_line, int p_to_line) { text.write[p_from_line].gutters.resize(gutter_count); } -//////////////////////////////////////////////////////////////////////////////// - -void TextEdit::_update_scrollbars() { - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, cache.style_normal->get_margin(SIDE_TOP))); - v_scroll->set_end(Point2(size.width, size.height - cache.style_normal->get_margin(SIDE_TOP) - cache.style_normal->get_margin(SIDE_BOTTOM))); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - - int visible_rows = get_visible_rows(); - int total_rows = get_total_visible_rows(); - if (scroll_past_end_of_file_enabled) { - total_rows += visible_rows - 1; - } - - int visible_width = size.width - cache.style_normal->get_minimum_size().width; - int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding; - - if (draw_minimap) { - total_width += cache.minimap_width; - } - - updating_scrolls = true; - - if (total_rows > visible_rows) { - v_scroll->show(); - v_scroll->set_max(total_rows + get_visible_rows_offset()); - v_scroll->set_page(visible_rows + get_visible_rows_offset()); - if (smooth_scroll_enabled) { - v_scroll->set_step(0.25); - } else { - v_scroll->set_step(1); - } - set_v_scroll(get_v_scroll()); - - } else { - cursor.line_ofs = 0; - cursor.wrap_ofs = 0; - v_scroll->set_value(0); - v_scroll->hide(); - } - - if (total_width > visible_width && !is_wrap_enabled()) { - h_scroll->show(); - h_scroll->set_max(total_width); - h_scroll->set_page(visible_width); - if (cursor.x_ofs > (total_width - visible_width)) { - cursor.x_ofs = (total_width - visible_width); - } - if (fabs(h_scroll->get_value() - (double)cursor.x_ofs) >= 1) { - h_scroll->set_value(cursor.x_ofs); - } - - } else { - cursor.x_ofs = 0; - h_scroll->set_value(0); - h_scroll->hide(); - } - - updating_scrolls = false; -} - -void TextEdit::_click_selection_held() { - // Warning: is_mouse_button_pressed(MOUSE_BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD - // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem. - // I'm unsure if there's an actual fix that doesn't have a ton of side effects. - if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { - switch (selection.selecting_mode) { - case SelectionMode::SELECTION_MODE_POINTER: { - _update_selection_mode_pointer(); - } break; - case SelectionMode::SELECTION_MODE_WORD: { - _update_selection_mode_word(); - } break; - case SelectionMode::SELECTION_MODE_LINE: { - _update_selection_mode_line(); - } break; - default: { - break; - } - } - } else { - click_select_held->stop(); - } -} - -Point2 TextEdit::_get_local_mouse_pos() const { - Point2 mp = get_local_mouse_position(); - if (is_layout_rtl()) { - mp.x = get_size().width - mp.x; - } - return mp; -} - -void TextEdit::_update_selection_mode_pointer() { - dragging_selection = true; - Point2 mp = _get_local_mouse_pos(); - - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - - select(selection.selecting_line, selection.selecting_column, row, col); - - cursor_set_line(row, false); - cursor_set_column(col); - update(); - - click_select_held->start(); -} - -void TextEdit::_update_selection_mode_word() { - dragging_selection = true; - Point2 mp = _get_local_mouse_pos(); - - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - - String line = text[row]; - int cursor_pos = CLAMP(col, 0, line.length()); - int beg = cursor_pos; - int end = beg; - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(row)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].x < cursor_pos && words[i].y > cursor_pos) { - beg = words[i].x; - end = words[i].y; - break; - } - } - - // Initial selection. - if (!selection.active) { - select(row, beg, row, end); - selection.selecting_column = beg; - selection.selected_word_beg = beg; - selection.selected_word_end = end; - selection.selected_word_origin = beg; - cursor_set_line(selection.to_line, false); - cursor_set_column(selection.to_column); - } else { - if ((col <= selection.selected_word_origin && row == selection.selecting_line) || row < selection.selecting_line) { - selection.selecting_column = selection.selected_word_end; - select(row, beg, selection.selecting_line, selection.selected_word_end); - cursor_set_line(selection.from_line, false); - cursor_set_column(selection.from_column); - } else { - selection.selecting_column = selection.selected_word_beg; - select(selection.selecting_line, selection.selected_word_beg, row, end); - cursor_set_line(selection.to_line, false); - cursor_set_column(selection.to_column); - } - } - - update(); - - click_select_held->start(); -} - -void TextEdit::_update_selection_mode_line() { - dragging_selection = true; - Point2 mp = _get_local_mouse_pos(); - - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - - col = 0; - if (row < selection.selecting_line) { - // Cursor is above us. - cursor_set_line(row - 1, false); - selection.selecting_column = text[selection.selecting_line].length(); - } else { - // Cursor is below us. - cursor_set_line(row + 1, false); - selection.selecting_column = 0; - col = text[row].length(); - } - cursor_set_column(0); - - select(selection.selecting_line, selection.selecting_column, row, col); - update(); - - click_select_held->start(); -} - -void TextEdit::_update_minimap_click() { - Point2 mp = _get_local_mouse_pos(); - - int xmargin_end = get_size().width - cache.style_normal->get_margin(SIDE_RIGHT); - if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) { - minimap_clicked = false; - return; - } - minimap_clicked = true; - dragging_minimap = true; - - int row; - _get_minimap_mouse_row(Point2i(mp.x, mp.y), row); - - if (row >= get_first_visible_line() && (row < get_last_full_visible_line() || row >= (text.size() - 1))) { - minimap_scroll_ratio = v_scroll->get_as_ratio(); - minimap_scroll_click_pos = mp.y; - can_drag_minimap = true; - return; - } - - int wi; - int first_line = row - num_lines_from_rows(row, 0, -get_visible_rows() / 2, wi) + 1; - double delta = get_scroll_pos_for_line(first_line, wi) - get_v_scroll(); - if (delta < 0) { - _scroll_up(-delta); - } else { - _scroll_down(delta); - } -} - -void TextEdit::_update_minimap_drag() { - if (!can_drag_minimap) { - return; - } - - int control_height = _get_control_height(); - int scroll_height = v_scroll->get_max() * (minimap_char_size.y + minimap_line_spacing); - if (control_height > scroll_height) { - control_height = scroll_height; - } - - Point2 mp = _get_local_mouse_pos(); - - double diff = (mp.y - minimap_scroll_click_pos) / control_height; - v_scroll->set_as_ratio(minimap_scroll_ratio + diff); -} +/////////////////////////////////////////////////////////////////////////////// +/// TEXT EDIT /// +/////////////////////////////////////////////////////////////////////////////// void TextEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_caches(); - if (cursor_changed_dirty) { - MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit"); + if (caret_pos_dirty) { + MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed"); } if (text_changed_dirty) { MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); } - _update_wrap_at(true); + _update_wrap_at_column(true); } break; case NOTIFICATION_RESIZED: { _update_scrollbars(); - _update_wrap_at(); + _update_wrap_at_column(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { if (is_visible()) { @@ -511,7 +275,7 @@ void TextEdit::_notification(int p_what) { case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_THEME_CHANGED: { _update_caches(); - _update_wrap_at(true); + _update_wrap_at_column(true); } break; case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; @@ -546,8 +310,8 @@ void TextEdit::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { if (first_draw) { - // Size may not be the final one, so attempts to ensure cursor was visible may have failed. - adjust_viewport_to_cursor(); + // Size may not be the final one, so attempts to ensure caret was visible may have failed. + adjust_viewport_to_caret(); first_draw = false; } @@ -564,34 +328,32 @@ void TextEdit::_notification(int p_what) { draw_caret = false; } - cache.minimap_width = 0; - if (draw_minimap) { - cache.minimap_width = minimap_width; - } - _update_scrollbars(); RID ci = get_canvas_item(); RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); - int xmargin_beg = cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding; + int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding; - int xmargin_end = size.width - cache.style_normal->get_margin(SIDE_RIGHT) - cache.minimap_width; + int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT); + if (draw_minimap) { + xmargin_end -= minimap_width; + } // Let's do it easy for now. - cache.style_normal->draw(ci, Rect2(Point2(), size)); - if (readonly) { - cache.style_readonly->draw(ci, Rect2(Point2(), size)); + style_normal->draw(ci, Rect2(Point2(), size)); + if (!editable) { + style_readonly->draw(ci, Rect2(Point2(), size)); draw_caret = false; } if (has_focus()) { - cache.style_focus->draw(ci, Rect2(Point2(), size)); + style_focus->draw(ci, Rect2(Point2(), size)); } - int visible_rows = get_visible_rows() + 1; + int visible_rows = get_visible_line_count() + 1; - Color color = readonly ? cache.font_readonly_color : cache.font_color; + Color color = !editable ? font_readonly_color : font_color; - if (cache.background_color.a > 0.01) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color); + if (background_color.a > 0.01) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), background_color); } int brace_open_match_line = -1; @@ -603,10 +365,10 @@ void TextEdit::_notification(int p_what) { bool brace_close_matching = false; bool brace_close_mismatch = false; - if (highlight_matching_braces_enabled && cursor.line >= 0 && cursor.line < text.size() && cursor.column >= 0) { - if (cursor.column < text[cursor.line].length()) { + if (highlight_matching_braces_enabled && caret.line >= 0 && caret.line < text.size() && caret.column >= 0) { + if (caret.column < text[caret.line].length()) { // Check for open. - char32_t c = text[cursor.line][cursor.column]; + char32_t c = text[caret.line][caret.column]; char32_t closec = 0; if (c == '[') { @@ -620,8 +382,8 @@ void TextEdit::_notification(int p_what) { if (closec != 0) { int stack = 1; - for (int i = cursor.line; i < text.size(); i++) { - int from = i == cursor.line ? cursor.column + 1 : 0; + for (int i = caret.line; i < text.size(); i++) { + int from = i == caret.line ? caret.column + 1 : 0; for (int j = from; j < text[i].length(); j++) { char32_t cc = text[i][j]; // Ignore any brackets inside a string. @@ -671,8 +433,8 @@ void TextEdit::_notification(int p_what) { } } - if (cursor.column > 0) { - char32_t c = text[cursor.line][cursor.column - 1]; + if (caret.column > 0) { + char32_t c = text[caret.line][caret.column - 1]; char32_t closec = 0; if (c == ']') { @@ -686,8 +448,8 @@ void TextEdit::_notification(int p_what) { if (closec != 0) { int stack = 1; - for (int i = cursor.line; i >= 0; i--) { - int from = i == cursor.line ? cursor.column - 2 : text[i].length() - 1; + for (int i = caret.line; i >= 0; i--) { + int from = i == caret.line ? caret.column - 2 : text[i].length() - 1; for (int j = from; j >= 0; j--) { char32_t cc = text[i][j]; // Ignore any brackets inside a string. @@ -739,22 +501,20 @@ void TextEdit::_notification(int p_what) { } // Get the highlighted words. - String highlighted_text = get_selection_text(); + String highlighted_text = get_selected_text(); // Check if highlighted words contain only whitespaces (tabs or spaces). bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String(); - int cursor_wrap_index = get_cursor_wrap_index(); - - //FontDrawer drawer(cache.font, Color(1, 1, 1)); + const int caret_wrap_index = get_caret_wrap_index(); int first_visible_line = get_first_visible_line() - 1; int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); - draw_amount += times_line_wraps(first_visible_line + 1); + draw_amount += get_line_wrap_count(first_visible_line + 1); // minimap if (draw_minimap) { - int minimap_visible_lines = _get_minimap_visible_rows(); + int minimap_visible_lines = get_minimap_visible_lines(); int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); int minimap_tab_size = minimap_char_size.x * text.get_tab_size(); @@ -765,20 +525,19 @@ void TextEdit::_notification(int p_what) { // calculate the first line. int num_lines_before = round((viewport_offset_y) / minimap_line_height); - int wi; int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line; if (minimap_line >= 0) { - minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi); + minimap_line -= get_next_visible_line_index_offset_from(first_visible_line, 0, -num_lines_before).x; minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0); } - int minimap_draw_amount = minimap_visible_lines + times_line_wraps(minimap_line + 1); + int minimap_draw_amount = minimap_visible_lines + get_line_wrap_count(minimap_line + 1); // draw the minimap - Color viewport_color = (cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1); + Color viewport_color = (background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1); if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, viewport_offset_y, cache.minimap_width, viewport_height), viewport_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, viewport_offset_y, minimap_width, viewport_height), viewport_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, cache.minimap_width, viewport_height), viewport_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), viewport_offset_y, minimap_width, viewport_height), viewport_color); } for (int i = 0; i < minimap_draw_amount; i++) { minimap_line++; @@ -787,7 +546,7 @@ void TextEdit::_notification(int p_what) { break; } - while (is_line_hidden(minimap_line)) { + while (_is_line_hidden(minimap_line)) { minimap_line++; if (minimap_line < 0 || minimap_line >= (int)text.size()) { break; @@ -802,13 +561,13 @@ void TextEdit::_notification(int p_what) { Color line_background_color = text.get_line_background_color(minimap_line); line_background_color.a *= 0.6; - Color current_color = cache.font_color; - if (readonly) { - current_color = cache.font_readonly_color; + Color current_color = font_color; + if (!editable) { + current_color = font_readonly_color; } - Vector<String> wrap_rows = get_wrap_rows_text(minimap_line); - int line_wrap_amount = times_line_wraps(minimap_line); + Vector<String> wrap_rows = get_line_wrapped_text(minimap_line); + int line_wrap_amount = get_line_wrap_count(minimap_line); int last_wrap_column = 0; for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) { @@ -821,7 +580,7 @@ void TextEdit::_notification(int p_what) { const String &str = wrap_rows[line_wrap_index]; int indent_px = line_wrap_index != 0 ? get_indent_level(minimap_line) : 0; - if (indent_px >= wrap_at) { + if (indent_px >= wrap_at_column) { indent_px = 0; } indent_px = minimap_char_size.x * indent_px; @@ -830,17 +589,17 @@ void TextEdit::_notification(int p_what) { last_wrap_column += wrap_rows[line_wrap_index - 1].length(); } - if (minimap_line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { + if (minimap_line == caret.line && caret_wrap_index == line_wrap_index && highlight_current_line) { if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, i * 3, cache.minimap_width, 2), cache.current_line_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, i * 3, minimap_width, 2), current_line_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), cache.current_line_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, minimap_width, 2), current_line_color); } } else if (line_background_color != Color(0, 0, 0, 0)) { if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - cache.minimap_width, i * 3, cache.minimap_width, 2), line_background_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - (xmargin_end + 2) - minimap_width, i * 3, minimap_width, 2), line_background_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, cache.minimap_width, 2), line_background_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2((xmargin_end + 2), i * 3, minimap_width, 2), line_background_color); } } @@ -850,8 +609,8 @@ void TextEdit::_notification(int p_what) { for (int j = 0; j < str.length(); j++) { if (color_map.has(last_wrap_column + j)) { current_color = color_map[last_wrap_column + j].get("color"); - if (readonly) { - current_color.a = cache.font_readonly_color.a; + if (!editable) { + current_color.a = font_readonly_color.a; } } color = current_color; @@ -861,7 +620,7 @@ void TextEdit::_notification(int p_what) { } int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs; - bool out_of_bounds = (xpos >= xmargin_end + cache.minimap_width); + bool out_of_bounds = (xpos >= xmargin_end + minimap_width); bool is_whitespace = _is_whitespace(str[j]); if (!is_whitespace) { @@ -912,18 +671,17 @@ void TextEdit::_notification(int p_what) { int top_limit_y = 0; int bottom_limit_y = get_size().height; - if (readonly) { - top_limit_y += cache.style_readonly->get_margin(SIDE_TOP); - bottom_limit_y -= cache.style_readonly->get_margin(SIDE_BOTTOM); + if (!editable) { + top_limit_y += style_readonly->get_margin(SIDE_TOP); + bottom_limit_y -= style_readonly->get_margin(SIDE_BOTTOM); } else { - top_limit_y += cache.style_normal->get_margin(SIDE_TOP); - bottom_limit_y -= cache.style_normal->get_margin(SIDE_BOTTOM); + top_limit_y += style_normal->get_margin(SIDE_TOP); + bottom_limit_y -= style_normal->get_margin(SIDE_BOTTOM); } // draw main text - cursor.visible = false; - const int caret_wrap_index = get_cursor_wrap_index(); - int row_height = get_row_height(); + caret.visible = false; + int row_height = get_line_height(); int line = first_visible_line; for (int i = 0; i < draw_amount; i++) { line++; @@ -932,7 +690,7 @@ void TextEdit::_notification(int p_what) { continue; } - while (is_line_hidden(line)) { + while (_is_line_hidden(line)) { line++; if (line < 0 || line >= (int)text.size()) { break; @@ -946,12 +704,12 @@ void TextEdit::_notification(int p_what) { Dictionary color_map = _get_line_syntax_highlighting(line); // Ensure we at least use the font color. - Color current_color = readonly ? cache.font_readonly_color : cache.font_color; + Color current_color = !editable ? font_readonly_color : font_color; const Ref<TextParagraph> ldata = text.get_line_data(line); - Vector<String> wrap_rows = get_wrap_rows_text(line); - int line_wrap_amount = times_line_wraps(line); + Vector<String> wrap_rows = get_line_wrapped_text(line); + int line_wrap_amount = get_line_wrap_count(line); for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) { if (line_wrap_index != 0) { @@ -962,21 +720,21 @@ void TextEdit::_notification(int p_what) { } const String &str = wrap_rows[line_wrap_index]; - int char_margin = xmargin_beg - cursor.x_ofs; + int char_margin = xmargin_beg - caret.x_ofs; int ofs_x = 0; int ofs_y = 0; - if (readonly) { - ofs_x = cache.style_readonly->get_offset().x / 2; - ofs_x -= cache.style_normal->get_offset().x / 2; - ofs_y = cache.style_readonly->get_offset().y / 2; + if (!editable) { + ofs_x = style_readonly->get_offset().x / 2; + ofs_x -= style_normal->get_offset().x / 2; + ofs_y = style_readonly->get_offset().y / 2; } else { - ofs_y = cache.style_normal->get_offset().y / 2; + ofs_y = style_normal->get_offset().y / 2; } - ofs_y += i * row_height + cache.line_spacing / 2; - ofs_y -= cursor.wrap_ofs * row_height; - ofs_y -= get_v_scroll_offset() * row_height; + ofs_y += i * row_height + line_spacing / 2; + ofs_y -= caret.wrap_ofs * row_height; + ofs_y -= _get_v_scroll_offset() * row_height; bool clipped = false; if (ofs_y + row_height < top_limit_y) { @@ -1001,30 +759,30 @@ void TextEdit::_notification(int p_what) { if (str.length() == 0) { // Draw line background if empty as we won't loop at all. - if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { + if (line == caret.line && caret_wrap_index == line_wrap_index && highlight_current_line) { if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), cache.current_line_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), current_line_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), cache.current_line_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), current_line_color); } } // Give visual indication of empty selected line. if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) { - int char_w = cache.font->get_char_size(' ', 0, cache.font_size).width; + int char_w = font->get_char_size(' ', 0, font_size).width; if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), cache.selection_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, row_height), cache.selection_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, row_height), selection_color); } } } else { // If it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later. - if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) { + if (line == caret.line && caret_wrap_index == line_wrap_index && highlight_current_line) { if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), cache.current_line_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - ofs_x - xmargin_end, ofs_y, xmargin_end, row_height), current_line_color); } else { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), cache.current_line_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, row_height), current_line_color); } } } @@ -1032,7 +790,7 @@ void TextEdit::_notification(int p_what) { if (line_wrap_index == 0) { // Only do these if we are on the first wrapped part of a line. - int gutter_offset = cache.style_normal->get_margin(SIDE_LEFT); + int gutter_offset = style_normal->get_margin(SIDE_LEFT); for (int g = 0; g < gutters.size(); g++) { const GutterInfo gutter = gutters[g]; @@ -1049,11 +807,11 @@ void TextEdit::_notification(int p_what) { Ref<TextLine> tl; tl.instantiate(); - tl->add_string(text, cache.font, cache.font_size); + tl->add_string(text, font, font_size); int yofs = ofs_y + (row_height - tl->get_size().y) / 2; - if (cache.outline_size > 0 && cache.outline_color.a > 0) { - tl->draw_outline(ci, Point2(gutter_offset + ofs_x, yofs), cache.outline_size, cache.outline_color); + if (outline_size > 0 && outline_color.a > 0) { + tl->draw_outline(ci, Point2(gutter_offset + ofs_x, yofs), outline_size, outline_color); } tl->draw(ci, Point2(gutter_offset + ofs_x, yofs), get_line_gutter_item_color(line, g)); } break; @@ -1105,7 +863,7 @@ void TextEdit::_notification(int p_what) { // Draw line. RID rid = ldata->get_line_rid(line_wrap_index); - float text_height = TS->shaped_text_get_size(rid).y + cache.font->get_spacing(Font::SPACING_TOP) + cache.font->get_spacing(Font::SPACING_BOTTOM); + float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(Font::SPACING_TOP) + font->get_spacing(Font::SPACING_BOTTOM); if (rtl) { char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x; @@ -1127,7 +885,7 @@ void TextEdit::_notification(int p_what) { if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - draw_rect(rect, cache.selection_color, true); + draw_rect(rect, selection_color, true); } } @@ -1147,8 +905,8 @@ void TextEdit::_notification(int p_what) { } else if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - draw_rect(rect, cache.search_result_color, true); - draw_rect(rect, cache.search_result_border_color, false); + draw_rect(rect, search_result_color, true); + draw_rect(rect, search_result_border_color, false); } search_text_col = _get_column_pos_of_word(search_text, str, search_flags, search_text_col + 1); @@ -1170,7 +928,7 @@ void TextEdit::_notification(int p_what) { } else if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - draw_rect(rect, cache.word_highlighted_color); + draw_rect(rect, word_highlighted_color); } highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_text_col + 1); @@ -1193,9 +951,9 @@ void TextEdit::_notification(int p_what) { } else if (rect.position.x + rect.size.x > xmargin_end) { rect.size.x = xmargin_end - rect.position.x; } - rect.position.y = TS->shaped_text_get_ascent(rid) + cache.font->get_underline_position(cache.font_size); - rect.size.y = cache.font->get_underline_thickness(cache.font_size); - draw_rect(rect, cache.font_selected_color); + rect.position.y = TS->shaped_text_get_ascent(rid) + font->get_underline_position(font_size); + rect.size.y = font->get_underline_thickness(font_size); + draw_rect(rect, font_selected_color); } highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1); @@ -1211,12 +969,12 @@ void TextEdit::_notification(int p_what) { ofs_y += ldata->get_line_ascent(line_wrap_index); int char_ofs = 0; - if (cache.outline_size > 0 && cache.outline_color.a > 0) { + if (outline_size > 0 && outline_color.a > 0) { for (int j = 0; j < gl_size; j++) { for (int k = 0; k < glyphs[j].repeat; k++) { if ((char_ofs + char_margin) >= xmargin_beg && (char_ofs + glyphs[j].advance + char_margin) <= xmargin_end) { if (glyphs[j].font_rid != RID()) { - TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, cache.outline_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, cache.outline_color); + TS->font_draw_glyph_outline(glyphs[j].font_rid, ci, glyphs[j].font_size, outline_size, Vector2(char_margin + char_ofs + ofs_x + glyphs[j].x_off, ofs_y + glyphs[j].y_off), glyphs[j].index, outline_color); } } char_ofs += glyphs[j].advance; @@ -1230,8 +988,8 @@ void TextEdit::_notification(int p_what) { for (int j = 0; j < gl_size; j++) { if (color_map.has(glyphs[j].start)) { current_color = color_map[glyphs[j].start].get("color"); - if (readonly && current_color.a > cache.font_readonly_color.a) { - current_color.a = cache.font_readonly_color.a; + if (!editable && current_color.a > font_readonly_color.a) { + current_color.a = font_readonly_color.a; } } @@ -1240,7 +998,7 @@ void TextEdit::_notification(int p_what) { int sel_to = (line < selection.to_line) ? TS->shaped_text_get_range(rid).y : selection.to_column; if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && override_selected_font_color) { - current_color = cache.font_selected_color; + current_color = font_selected_color; } } @@ -1248,31 +1006,31 @@ void TextEdit::_notification(int p_what) { if (char_pos >= xmargin_beg) { if (highlight_matching_braces_enabled) { if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) || - (cursor.column == glyphs[j].start && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { + (caret.column == glyphs[j].start && caret.line == line && caret_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { if (brace_open_mismatch) { - current_color = cache.brace_mismatch_color; + current_color = brace_mismatch_color; } - Rect2 rect = Rect2(char_pos, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size)); + Rect2 rect = Rect2(char_pos, ofs_y + font->get_underline_position(font_size), glyphs[j].advance * glyphs[j].repeat, font->get_underline_thickness(font_size)); draw_rect(rect, current_color); } if ((brace_close_match_line == line && brace_close_match_column == glyphs[j].start) || - (cursor.column == glyphs[j].start + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) { + (caret.column == glyphs[j].start + 1 && caret.line == line && caret_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) { if (brace_close_mismatch) { - current_color = cache.brace_mismatch_color; + current_color = brace_mismatch_color; } - Rect2 rect = Rect2(char_pos, ofs_y + cache.font->get_underline_position(cache.font_size), glyphs[j].advance * glyphs[j].repeat, cache.font->get_underline_thickness(cache.font_size)); + Rect2 rect = Rect2(char_pos, ofs_y + font->get_underline_position(font_size), glyphs[j].advance * glyphs[j].repeat, font->get_underline_thickness(font_size)); draw_rect(rect, current_color); } } if (draw_tabs && ((glyphs[j].flags & TextServer::GRAPHEME_IS_TAB) == TextServer::GRAPHEME_IS_TAB)) { - int yofs = (text_height - cache.tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); - cache.tab_icon->draw(ci, Point2(char_pos, ofs_y + yofs), current_color); + int yofs = (text_height - tab_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); + tab_icon->draw(ci, Point2(char_pos, ofs_y + yofs), current_color); } else if (draw_spaces && ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE)) { - int yofs = (text_height - cache.space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); - int xofs = (glyphs[j].advance * glyphs[j].repeat - cache.space_icon->get_width()) / 2; - cache.space_icon->draw(ci, Point2(char_pos + xofs, ofs_y + yofs), current_color); + int yofs = (text_height - space_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); + int xofs = (glyphs[j].advance * glyphs[j].repeat - space_icon->get_width()) / 2; + space_icon->draw(ci, Point2(char_pos + xofs, ofs_y + yofs), current_color); } } @@ -1292,13 +1050,13 @@ void TextEdit::_notification(int p_what) { } // is_line_folded - if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && is_line_hidden(line + 1)) { - int xofs = char_ofs + char_margin + ofs_x + (cache.folded_eol_icon->get_width() / 2); + if (line_wrap_index == line_wrap_amount && line < text.size() - 1 && _is_line_hidden(line + 1)) { + int xofs = char_ofs + char_margin + ofs_x + (folded_eol_icon->get_width() / 2); if (xofs >= xmargin_beg && xofs < xmargin_end) { - int yofs = (text_height - cache.folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); - Color eol_color = cache.code_folding_color; + int yofs = (text_height - folded_eol_icon->get_height()) / 2 - ldata->get_line_ascent(line_wrap_index); + Color eol_color = code_folding_color; eol_color.a = 1; - cache.folded_eol_icon->draw(ci, Point2(xofs, ofs_y + yofs), eol_color); + folded_eol_icon->draw(ci, Point2(xofs, ofs_y + yofs), eol_color); } } @@ -1309,18 +1067,18 @@ void TextEdit::_notification(int p_what) { int caret_width = 1; #endif - if (!clipped && cursor.line == line && line_wrap_index == caret_wrap_index) { - cursor.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index); + if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) { + caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index); if (ime_text.length() == 0) { Rect2 l_caret, t_caret; TextServer::Direction l_dir, t_dir; if (str.length() != 0) { // Get carets. - TS->shaped_text_get_carets(rid, cursor.column, l_caret, l_dir, t_caret, t_dir); + TS->shaped_text_get_carets(rid, caret.column, l_caret, l_dir, t_caret, t_dir); } else { // No carets, add one at the start. - int h = cache.font->get_height(cache.font_size); + int h = font->get_height(font_size); if (rtl) { l_dir = TextServer::DIRECTION_RTL; l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h)); @@ -1331,20 +1089,20 @@ void TextEdit::_notification(int p_what) { } if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - cursor.draw_pos.x = char_margin + ofs_x + l_caret.position.x; + caret.draw_pos.x = char_margin + ofs_x + l_caret.position.x; } else { - cursor.draw_pos.x = char_margin + ofs_x + t_caret.position.x; + caret.draw_pos.x = char_margin + ofs_x + t_caret.position.x; } - if (cursor.draw_pos.x >= xmargin_beg && cursor.draw_pos.x < xmargin_end) { - cursor.visible = true; + if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) { + caret.visible = true; if (draw_caret) { - if (block_caret || insert_mode) { + if (caret_type == CaretType::CARET_TYPE_BLOCK || overtype_mode) { //Block or underline caret, draw trailing carets at full height. - int h = cache.font->get_height(cache.font_size); + int h = font->get_height(font_size); if (t_caret != Rect2()) { - if (insert_mode) { + if (overtype_mode) { t_caret.position.y = TS->shaped_text_get_descent(rid); t_caret.size.y = caret_width; } else { @@ -1353,9 +1111,9 @@ void TextEdit::_notification(int p_what) { } t_caret.position += Vector2(char_margin + ofs_x, ofs_y); - draw_rect(t_caret, cache.caret_color, false); + draw_rect(t_caret, caret_color, false); } else { // End of the line. - if (insert_mode) { + if (overtype_mode) { l_caret.position.y = TS->shaped_text_get_descent(rid); l_caret.size.y = caret_width; } else { @@ -1363,9 +1121,9 @@ void TextEdit::_notification(int p_what) { l_caret.size.y = h; } l_caret.position += Vector2(char_margin + ofs_x, ofs_y); - l_caret.size.x = cache.font->get_char_size('M', 0, cache.font_size).x; + l_caret.size.x = font->get_char_size('M', 0, font_size).x; - draw_rect(l_caret, cache.caret_color, false); + draw_rect(l_caret, caret_color, false); } } else { // Normal caret. @@ -1373,24 +1131,24 @@ void TextEdit::_notification(int p_what) { // Draw extra marker on top of mid caret. Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width); trect.position += Vector2(char_margin + ofs_x, ofs_y); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, cache.caret_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color); } l_caret.position += Vector2(char_margin + ofs_x, ofs_y); l_caret.size.x = caret_width; - draw_rect(l_caret, cache.caret_color); + draw_rect(l_caret, caret_color); t_caret.position += Vector2(char_margin + ofs_x, ofs_y); t_caret.size.x = caret_width; - draw_rect(t_caret, cache.caret_color); + draw_rect(t_caret, caret_color); } } } } else { { // IME Intermediate text range. - Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column, cursor.column + ime_text.length()); + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, caret.column, caret.column + ime_text.length()); for (int j = 0; j < sel.size(); j++) { Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height); if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { @@ -1403,13 +1161,13 @@ void TextEdit::_notification(int p_what) { rect.size.x = xmargin_end - rect.position.x; } rect.size.y = caret_width; - draw_rect(rect, cache.caret_color); - cursor.draw_pos.x = rect.position.x; + draw_rect(rect, caret_color); + caret.draw_pos.x = rect.position.x; } } { // IME caret. - Vector<Vector2> sel = TS->shaped_text_get_selection(rid, cursor.column + ime_selection.x, cursor.column + ime_selection.x + ime_selection.y); + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, caret.column + ime_selection.x, caret.column + ime_selection.x + ime_selection.y); for (int j = 0; j < sel.size(); j++) { Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, text_height); if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { @@ -1422,8 +1180,8 @@ void TextEdit::_notification(int p_what) { rect.size.x = xmargin_end - rect.position.x; } rect.size.y = caret_width * 3; - draw_rect(rect, cache.caret_color); - cursor.draw_pos.x = rect.position.x; + draw_rect(rect, caret_color); + caret.draw_pos.x = rect.position.x; } } } @@ -1434,7 +1192,7 @@ void TextEdit::_notification(int p_what) { if (has_focus()) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor.draw_pos, get_viewport()->get_window_id()); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + caret.draw_pos, get_viewport()->get_window_id()); } } } break; @@ -1447,26 +1205,26 @@ void TextEdit::_notification(int p_what) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + _get_cursor_pixel_pos(false), get_viewport()->get_window_id()); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + get_caret_draw_pos(), get_viewport()->get_window_id()); } if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - int cursor_start = -1; - int cursor_end = -1; + int caret_start = -1; + int caret_end = -1; if (!selection.active) { - String full_text = _base_get_text(0, 0, cursor.line, cursor.column); + String full_text = _base_get_text(0, 0, caret.line, caret.column); - cursor_start = full_text.length(); + caret_start = full_text.length(); } else { String pre_text = _base_get_text(0, 0, selection.from_line, selection.from_column); - String post_text = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + String post_text = get_selected_text(); - cursor_start = pre_text.length(); - cursor_end = cursor_start + post_text.length(); + caret_start = pre_text.length(); + caret_end = caret_start + post_text.length(); } - DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, cursor_start, cursor_end); + DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, caret_start, caret_end); } } break; case NOTIFICATION_FOCUS_EXIT: { @@ -1480,7 +1238,7 @@ void TextEdit::_notification(int p_what) { } ime_text = ""; ime_selection = Point2(); - text.invalidate_cache(cursor.line, cursor.column, ime_text); + text.invalidate_cache(caret.line, caret.column, ime_text); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); @@ -1492,642 +1250,19 @@ void TextEdit::_notification(int p_what) { ime_selection = DisplayServer::get_singleton()->ime_get_selection(); String t; - if (cursor.column >= 0) { - t = text[cursor.line].substr(0, cursor.column) + ime_text + text[cursor.line].substr(cursor.column, text[cursor.line].length()); + if (caret.column >= 0) { + t = text[caret.line].substr(0, caret.column) + ime_text + text[caret.line].substr(caret.column, text[caret.line].length()); } else { t = ime_text; } - text.invalidate_cache(cursor.line, cursor.column, t, structured_text_parser(st_parser, st_args, t)); + text.invalidate_cache(caret.line, caret.column, t, structured_text_parser(st_parser, st_args, t)); update(); } } break; } } -void TextEdit::backspace() { - ScriptInstance *si = get_script_instance(); - if (si && si->has_method("_backspace")) { - si->call("_backspace"); - return; - } - - if (readonly) { - return; - } - - if (cursor.column == 0 && cursor.line == 0) { - return; - } - - if (is_selection_active()) { - delete_selection(); - return; - } - - int prev_line = cursor.column ? cursor.line : cursor.line - 1; - int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length()); - - merge_gutters(cursor.line, prev_line); - - if (is_line_hidden(cursor.line)) { - set_line_as_hidden(prev_line, true); - } - _remove_text(prev_line, prev_column, cursor.line, cursor.column); - - cursor_set_line(prev_line, false, true); - cursor_set_column(prev_column); -} - -void TextEdit::_swap_current_input_direction() { - if (input_direction == TEXT_DIRECTION_LTR) { - input_direction = TEXT_DIRECTION_RTL; - } else { - input_direction = TEXT_DIRECTION_LTR; - } - cursor_set_column(cursor.column); - update(); -} - -void TextEdit::_new_line(bool p_split_current_line, bool p_above) { - if (readonly) { - return; - } - - begin_complex_operation(); - - bool first_line = false; - if (!p_split_current_line) { - if (p_above) { - if (cursor.line > 0) { - cursor_set_line(cursor.line - 1, false); - cursor_set_column(text[cursor.line].length()); - } else { - cursor_set_column(0); - first_line = true; - } - } else { - cursor_set_column(text[cursor.line].length()); - } - } - - insert_text_at_cursor("\n"); - - if (first_line) { - cursor_set_line(0); - } - - end_complex_operation(); -} - -void TextEdit::_move_cursor_left(bool p_select, bool p_move_by_word) { - // Handle selection - if (p_select) { - _pre_shift_selection(); - } else if (selection.active && !p_move_by_word) { - // If a selection is active, move cursor to start of selection - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - deselect(); - return; - } else { - deselect(); - } - - if (p_move_by_word) { - int cc = cursor.column; - - if (cc == 0 && cursor.line > 0) { - cursor_set_line(cursor.line - 1); - cursor_set_column(text[cursor.line].length()); - } else { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < cc) { - cc = words[i].x; - break; - } - } - cursor_set_column(cc); - } - } else { - // If the cursor is at the start of the line, and not on the first line, move it up to the end of the previous line. - if (cursor.column == 0) { - if (cursor.line > 0) { - cursor_set_line(cursor.line - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1)); - cursor_set_column(text[cursor.line].length()); - } - } else { - if (mid_grapheme_caret_enabled) { - cursor_set_column(cursor_get_column() - 1); - } else { - cursor_set_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); - } - } - } - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_right(bool p_select, bool p_move_by_word) { - // Handle selection - if (p_select) { - _pre_shift_selection(); - } else if (selection.active && !p_move_by_word) { - // If a selection is active, move cursor to end of selection - cursor_set_line(selection.to_line); - cursor_set_column(selection.to_column); - deselect(); - return; - } else { - deselect(); - } - - if (p_move_by_word) { - int cc = cursor.column; - - if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) { - cursor_set_line(cursor.line + 1); - cursor_set_column(0); - } else { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > cc) { - cc = words[i].y; - break; - } - } - cursor_set_column(cc); - } - } else { - // If we are at the end of the line, move the caret to the next line down. - if (cursor.column == text[cursor.line].length()) { - if (cursor.line < text.size() - 1) { - cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false); - cursor_set_column(0); - } - } else { - if (mid_grapheme_caret_enabled) { - cursor_set_column(cursor_get_column() + 1); - } else { - cursor_set_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), cursor_get_column())); - } - } - } - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_up(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - int cur_wrap_index = get_cursor_wrap_index(); - if (cur_wrap_index > 0) { - cursor_set_line(cursor.line, true, false, cur_wrap_index - 1); - } else if (cursor.line == 0) { - cursor_set_column(0); - } else { - int new_line = cursor.line - num_lines_from(cursor.line - 1, -1); - if (line_wraps(new_line)) { - cursor_set_line(new_line, true, false, times_line_wraps(new_line)); - } else { - cursor_set_line(new_line, true, false); - } - } - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_down(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - int cur_wrap_index = get_cursor_wrap_index(); - if (cur_wrap_index < times_line_wraps(cursor.line)) { - cursor_set_line(cursor.line, true, false, cur_wrap_index + 1); - } else if (cursor.line == get_last_unhidden_line()) { - cursor_set_column(text[cursor.line].length()); - } else { - int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1); - cursor_set_line(new_line, true, false, 0); - } - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_to_line_start(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - // Move cursor column to start of wrapped row and then to start of text. - Vector<String> rows = get_wrap_rows_text(cursor.line); - int wi = get_cursor_wrap_index(); - int row_start_col = 0; - for (int i = 0; i < wi; i++) { - row_start_col += rows[i].length(); - } - if (cursor.column == row_start_col || wi == 0) { - // Compute whitespace symbols sequence length. - int current_line_whitespace_len = 0; - while (current_line_whitespace_len < text[cursor.line].length()) { - char32_t c = text[cursor.line][current_line_whitespace_len]; - if (c != '\t' && c != ' ') { - break; - } - current_line_whitespace_len++; - } - - if (cursor_get_column() == current_line_whitespace_len) { - cursor_set_column(0); - } else { - cursor_set_column(current_line_whitespace_len); - } - } else { - cursor_set_column(row_start_col); - } - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_to_line_end(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - // Move cursor column to end of wrapped row and then to end of text. - Vector<String> rows = get_wrap_rows_text(cursor.line); - int wi = get_cursor_wrap_index(); - int row_end_col = -1; - for (int i = 0; i < wi + 1; i++) { - row_end_col += rows[i].length(); - } - if (wi == rows.size() - 1 || cursor.column == row_end_col) { - cursor_set_column(text[cursor.line].length()); - } else { - cursor_set_column(row_end_col); - } - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_page_up(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - int wi; - int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1; - cursor_set_line(n_line, true, false, wi); - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_page_down(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - int wi; - int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1; - cursor_set_line(n_line, true, false, wi); - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { - if (readonly) { - return; - } - - if (is_selection_active() || (!p_all_to_left && !p_word)) { - backspace(); - return; - } - - if (p_all_to_left) { - int cursor_current_column = cursor.column; - cursor.column = 0; - _remove_text(cursor.line, 0, cursor.line, cursor_current_column); - return; - } - - if (p_word) { - int line = cursor.line; - int column = cursor.column; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = words.size() - 1; i >= 0; i--) { - if (words[i].x < column) { - column = words[i].x; - break; - } - } - - _remove_text(line, column, cursor.line, cursor.column); - - cursor_set_line(line, false); - cursor_set_column(column); - return; - } -} - -void TextEdit::_delete(bool p_word, bool p_all_to_right) { - if (readonly) { - return; - } - - if (is_selection_active()) { - delete_selection(); - return; - } - int curline_len = text[cursor.line].length(); - - if (cursor.line == text.size() - 1 && cursor.column == curline_len) { - return; // Last line, last column: Nothing to do. - } - - int next_line = cursor.column < curline_len ? cursor.line : cursor.line + 1; - int next_column; - - if (p_all_to_right) { - // Delete everything to right of cursor - next_column = curline_len; - next_line = cursor.line; - } else if (p_word && cursor.column < curline_len - 1) { - // Delete next word to right of cursor - int line = cursor.line; - int column = cursor.column; - - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].y > column) { - column = words[i].y; - break; - } - } - - next_line = line; - next_column = column; - } else { - // Delete one character - next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; - if (mid_grapheme_caret_enabled) { - next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; - } else { - next_column = cursor.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(cursor.line)->get_rid(), (cursor.column)) : 0; - } - } - - _remove_text(cursor.line, cursor.column, next_line, next_column); - update(); -} - -void TextEdit::delete_selection() { - if (!is_selection_active()) { - return; - } - - selection.active = false; - selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line, false, false); - cursor_set_column(selection.from_column); - update(); -} - -void TextEdit::_move_cursor_document_start(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - cursor_set_line(0); - cursor_set_column(0); - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::_move_cursor_document_end(bool p_select) { - if (p_select) { - _pre_shift_selection(); - } else { - deselect(); - } - - cursor_set_line(get_last_unhidden_line(), true, false, 9999); - cursor_set_column(text[cursor.line].length()); - - if (p_select) { - _post_shift_selection(); - } -} - -void TextEdit::handle_unicode_input(uint32_t p_unicode) { - ScriptInstance *si = get_script_instance(); - if (si && si->has_method("_handle_unicode_input")) { - si->call("_handle_unicode_input", p_unicode); - return; - } - - bool had_selection = selection.active; - if (had_selection) { - begin_complex_operation(); - delete_selection(); - } - - // Remove the old character if in insert mode and no selection. - if (insert_mode && !had_selection) { - begin_complex_operation(); - - // Make sure we don't try and remove empty space. - if (cursor.column < get_line(cursor.line).length()) { - _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); - } - } - - const char32_t chr[2] = { (char32_t)p_unicode, 0 }; - insert_text_at_cursor(chr); - - if ((insert_mode && !had_selection) || (had_selection)) { - end_complex_operation(); - } -} - -void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const { - float rows = p_mouse.y; - rows -= cache.style_normal->get_margin(SIDE_TOP); - rows /= get_row_height(); - rows += get_v_scroll_offset(); - int first_vis_line = get_first_visible_line(); - int row = first_vis_line + Math::floor(rows); - int wrap_index = 0; - - if (is_wrap_enabled() || is_hiding_enabled()) { - int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1; - if (rows < 0) { - row = first_vis_line - f_ofs; - } else { - row = first_vis_line + f_ofs; - } - } - - if (row < 0) { - row = 0; - } - - int col = 0; - - if (row >= text.size()) { - row = text.size() - 1; - col = text[row].size(); - } else { - int colx = p_mouse.x - (cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); - colx += cursor.x_ofs; - col = get_char_pos_for_line(colx, row, wrap_index); - if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) { - // Move back one if we are at the end of the row. - Vector<String> rows2 = get_wrap_rows_text(row); - int row_end_col = 0; - for (int i = 0; i < wrap_index + 1; i++) { - row_end_col += rows2[i].length(); - } - if (col >= row_end_col) { - col -= 1; - } - } - - RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); - if (is_layout_rtl()) { - colx = TS->shaped_text_get_size(text_rid).x - colx; - } - col = TS->shaped_text_hit_test_position(text_rid, colx); - } - - r_row = row; - r_col = col; -} - -Vector2i TextEdit::_get_cursor_pixel_pos(bool p_adjust_viewport) { - if (p_adjust_viewport) { - adjust_viewport_to_cursor(); - } - int row = 1; - for (int i = get_first_visible_line(); i < cursor.line; i++) { - if (!is_line_hidden(i)) { - row += times_line_wraps(i) + 1; - } - } - row += cursor.wrap_ofs; - - // Calculate final pixel position - int y = (row - get_v_scroll_offset()) * get_row_height(); - int x = cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding - cursor.x_ofs; - - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; - RID text_rid = text.get_line_data(cursor.line)->get_line_rid(cursor.wrap_ofs); - TS->shaped_text_get_carets(text_rid, cursor.column, l_caret, l_dir, t_caret, t_dir); - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - x += l_caret.position.x; - } else { - x += t_caret.position.x; - } - - return Vector2i(x, y); -} - -void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const { - float rows = p_mouse.y; - rows -= cache.style_normal->get_margin(SIDE_TOP); - rows /= (minimap_char_size.y + minimap_line_spacing); - rows += get_v_scroll_offset(); - - // calculate visible lines - int minimap_visible_lines = _get_minimap_visible_rows(); - int visible_rows = get_visible_rows() + 1; - int first_visible_line = get_first_visible_line() - 1; - int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); - draw_amount += times_line_wraps(first_visible_line + 1); - int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); - - // calculate viewport size and y offset - int viewport_height = (draw_amount - 1) * minimap_line_height; - int control_height = _get_control_height() - viewport_height; - int viewport_offset_y = round(get_scroll_pos_for_line(first_visible_line) * control_height) / ((v_scroll->get_max() <= minimap_visible_lines) ? (minimap_visible_lines - draw_amount) : (v_scroll->get_max() - draw_amount)); - - // calculate the first line. - int num_lines_before = round((viewport_offset_y) / minimap_line_height); - int wi; - int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line; - if (first_visible_line > 0 && minimap_line >= 0) { - minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi); - minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0); - } else { - minimap_line = 0; - } - - int row = minimap_line + Math::floor(rows); - int wrap_index = 0; - - if (is_wrap_enabled() || is_hiding_enabled()) { - int f_ofs = num_lines_from_rows(minimap_line, cursor.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1; - if (rows < 0) { - row = minimap_line - f_ofs; - } else { - row = minimap_line + f_ofs; - } - } - - if (row < 0) { - row = 0; - } - - if (row >= text.size()) { - row = text.size() - 1; - } - - r_row = row; -} - -bool TextEdit::is_dragging_cursor() const { - return dragging_selection || dragging_minimap; -} - void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { ERR_FAIL_COND(p_gui_input.is_null()); @@ -2178,10 +1313,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { _reset_caret_blink_timer(); - int row, col; - _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); + Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + int row = pos.y; + int col = pos.x; - int left_margin = cache.style_normal->get_margin(SIDE_LEFT); + int left_margin = style_normal->get_margin(SIDE_LEFT); for (int i = 0; i < gutters.size(); i++) { if (!gutters[i].draw || gutters[i].width <= 0) { continue; @@ -2203,20 +1339,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } - int prev_col = cursor.column; - int prev_line = cursor.line; + int prev_col = caret.column; + int prev_line = caret.line; - cursor_set_line(row, false, false); - cursor_set_column(col); + set_caret_line(row, false, false); + set_caret_column(col); - if (mb->is_shift_pressed() && (cursor.column != prev_col || cursor.line != prev_line)) { + if (mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) { if (!selection.active) { selection.active = true; selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER; selection.from_column = prev_col; selection.from_line = prev_line; - selection.to_column = cursor.column; - selection.to_line = cursor.line; + selection.to_column = caret.column; + selection.to_line = caret.line; if (selection.from_line > selection.to_line || (selection.from_line == selection.to_line && selection.from_column > selection.to_column)) { SWAP(selection.from_column, selection.to_column); @@ -2229,21 +1365,21 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { selection.selecting_column = prev_col; update(); } else { - if (cursor.line < selection.selecting_line || (cursor.line == selection.selecting_line && cursor.column < selection.selecting_column)) { + if (caret.line < selection.selecting_line || (caret.line == selection.selecting_line && caret.column < selection.selecting_column)) { if (selection.shiftclick_left) { selection.shiftclick_left = !selection.shiftclick_left; } - selection.from_column = cursor.column; - selection.from_line = cursor.line; + selection.from_column = caret.column; + selection.from_line = caret.line; - } else if (cursor.line > selection.selecting_line || (cursor.line == selection.selecting_line && cursor.column > selection.selecting_column)) { + } else if (caret.line > selection.selecting_line || (caret.line == selection.selecting_line && caret.column > selection.selecting_column)) { if (!selection.shiftclick_left) { SWAP(selection.from_column, selection.to_column); SWAP(selection.from_line, selection.to_line); selection.shiftclick_left = !selection.shiftclick_left; } - selection.to_column = cursor.column; - selection.to_line = cursor.line; + selection.to_column = caret.column; + selection.to_line = caret.line; } else { selection.active = false; @@ -2266,7 +1402,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE; _update_selection_mode_line(); last_dblclk = 0; - } else if (mb->is_double_click() && text[cursor.line].length()) { + } else if (mb->is_double_click() && text[caret.line].length()) { // Double-click select word. selection.selecting_mode = SelectionMode::SELECTION_MODE_WORD; _update_selection_mode_word(); @@ -2280,11 +1416,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) { _reset_caret_blink_timer(); - int row, col; - _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); + Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + int row = pos.y; + int col = pos.x; - if (is_right_click_moving_caret()) { - if (is_selection_active()) { + if (is_move_caret_on_right_click_enabled()) { + if (has_selection()) { int from_line = get_selection_from_line(); int to_line = get_selection_to_line(); int from_column = get_selection_from_column(); @@ -2295,13 +1432,13 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { deselect(); } } - if (!is_selection_active()) { - cursor_set_line(row, true, false); - cursor_set_column(col); + if (!has_selection()) { + set_caret_line(row, true, false); + set_caret_column(col); } } - _ensure_menu(); + _generate_context_menu(); menu->set_position(get_screen_transform().xform(mpos)); menu->set_size(Vector2(1, 1)); menu->popup(); @@ -2500,8 +1637,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // MISC. if (k->is_action("ui_menu", true)) { if (context_menu_enabled) { - _ensure_menu(); - menu->set_position(get_screen_transform().xform(_get_cursor_pixel_pos())); + _generate_context_menu(); + adjust_viewport_to_caret(); + menu->set_position(get_screen_transform().xform(get_caret_draw_pos())); menu->set_size(Vector2(1, 1)); menu->popup(); menu->grab_focus(); @@ -2510,7 +1648,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } if (k->is_action("ui_text_toggle_insert_mode", true)) { - set_insert_mode(!insert_mode); + set_overtype_mode_enabled(!overtype_mode); accept_event(); return; } @@ -2520,85 +1658,85 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - // CURSOR MOVEMENT + // CARET MOVEMENT k = k->duplicate(); bool shift_pressed = k->is_shift_pressed(); // Remove shift or else actions will not match. Use above variable for selection. k->set_shift_pressed(false); - // CURSOR MOVEMENT - LEFT, RIGHT. + // CARET MOVEMENT - LEFT, RIGHT. if (k->is_action("ui_text_caret_word_left", true)) { - _move_cursor_left(shift_pressed, true); + _move_caret_left(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_left", true)) { - _move_cursor_left(shift_pressed, false); + _move_caret_left(shift_pressed, false); accept_event(); return; } if (k->is_action("ui_text_caret_word_right", true)) { - _move_cursor_right(shift_pressed, true); + _move_caret_right(shift_pressed, true); accept_event(); return; } if (k->is_action("ui_text_caret_right", true)) { - _move_cursor_right(shift_pressed, false); + _move_caret_right(shift_pressed, false); accept_event(); return; } - // CURSOR MOVEMENT - UP, DOWN. + // CARET MOVEMENT - UP, DOWN. if (k->is_action("ui_text_caret_up", true)) { - _move_cursor_up(shift_pressed); + _move_caret_up(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_down", true)) { - _move_cursor_down(shift_pressed); + _move_caret_down(shift_pressed); accept_event(); return; } - // CURSOR MOVEMENT - DOCUMENT START/END. + // CARET MOVEMENT - DOCUMENT START/END. if (k->is_action("ui_text_caret_document_start", true)) { // && shift_pressed) { - _move_cursor_document_start(shift_pressed); + _move_caret_document_start(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_document_end", true)) { // && shift_pressed) { - _move_cursor_document_end(shift_pressed); + _move_caret_document_end(shift_pressed); accept_event(); return; } - // CURSOR MOVEMENT - LINE START/END. + // CARET MOVEMENT - LINE START/END. if (k->is_action("ui_text_caret_line_start", true)) { - _move_cursor_to_line_start(shift_pressed); + _move_caret_to_line_start(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_line_end", true)) { - _move_cursor_to_line_end(shift_pressed); + _move_caret_to_line_end(shift_pressed); accept_event(); return; } - // CURSOR MOVEMENT - PAGE UP/DOWN. + // CARET MOVEMENT - PAGE UP/DOWN. if (k->is_action("ui_text_caret_page_up", true)) { - _move_cursor_page_up(shift_pressed); + _move_caret_page_up(shift_pressed); accept_event(); return; } if (k->is_action("ui_text_caret_page_down", true)) { - _move_cursor_page_down(shift_pressed); + _move_caret_page_down(shift_pressed); accept_event(); return; } - // Handle Unicode (if no modifiers active). - if (allow_unicode_handling && !readonly && k->get_unicode() >= 32) { + // Handle Unicode (if no modifiers active). Tab has a value of 0x09. + if (allow_unicode_handling && editable && (k->get_unicode() >= 32 || k->get_keycode() == KEY_TAB)) { handle_unicode_input(k->get_unicode()); accept_event(); return; @@ -2606,902 +1744,486 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } -void TextEdit::_scroll_up(real_t p_delta) { - if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta)) { - scrolling = false; - minimap_clicked = false; - } - - if (scrolling) { - target_v_scroll = (target_v_scroll - p_delta); - } else { - target_v_scroll = (get_v_scroll() - p_delta); - } - - if (smooth_scroll_enabled) { - if (target_v_scroll <= 0) { - target_v_scroll = 0; - } - if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { - v_scroll->set_value(target_v_scroll); - } else { - scrolling = true; - set_physics_process_internal(true); - } +/* Input actions. */ +void TextEdit::_swap_current_input_direction() { + if (input_direction == TEXT_DIRECTION_LTR) { + input_direction = TEXT_DIRECTION_RTL; } else { - set_v_scroll(target_v_scroll); + input_direction = TEXT_DIRECTION_LTR; } + set_caret_column(caret.column); + update(); } -void TextEdit::_scroll_down(real_t p_delta) { - if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta)) { - scrolling = false; - minimap_clicked = false; +void TextEdit::_new_line(bool p_split_current_line, bool p_above) { + if (!editable) { + return; } - if (scrolling) { - target_v_scroll = (target_v_scroll + p_delta); - } else { - target_v_scroll = (get_v_scroll() + p_delta); - } + begin_complex_operation(); - if (smooth_scroll_enabled) { - int max_v_scroll = round(v_scroll->get_max() - v_scroll->get_page()); - if (target_v_scroll > max_v_scroll) { - target_v_scroll = max_v_scroll; - } - if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { - v_scroll->set_value(target_v_scroll); + bool first_line = false; + if (!p_split_current_line) { + if (p_above) { + if (caret.line > 0) { + set_caret_line(caret.line - 1, false); + set_caret_column(text[caret.line].length()); + } else { + set_caret_column(0); + first_line = true; + } } else { - scrolling = true; - set_physics_process_internal(true); + set_caret_column(text[caret.line].length()); } - } else { - set_v_scroll(target_v_scroll); } -} -void TextEdit::_pre_shift_selection() { - if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) { - selection.selecting_line = cursor.line; - selection.selecting_column = cursor.column; - selection.active = true; - } - - selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; -} - -void TextEdit::_post_shift_selection() { - if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) { - select(selection.selecting_line, selection.selecting_column, cursor.line, cursor.column); - update(); - } - - selection.selecting_text = true; -} - -void TextEdit::_scroll_lines_up() { - scrolling = false; - minimap_clicked = false; + insert_text_at_caret("\n"); - // Adjust the vertical scroll. - set_v_scroll(get_v_scroll() - 1); - - // Adjust the cursor to viewport. - if (!selection.active) { - int cur_line = cursor.line; - int cur_wrap = get_cursor_wrap_index(); - int last_vis_line = get_last_full_visible_line(); - int last_vis_wrap = get_last_full_visible_line_wrap_index(); - - if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) { - cursor_set_line(last_vis_line, false, false, last_vis_wrap); - } + if (first_line) { + set_caret_line(0); } -} - -void TextEdit::_scroll_lines_down() { - scrolling = false; - minimap_clicked = false; - - // Adjust the vertical scroll. - set_v_scroll(get_v_scroll() + 1); - // Adjust the cursor to viewport. - if (!selection.active) { - int cur_line = cursor.line; - int cur_wrap = get_cursor_wrap_index(); - int first_vis_line = get_first_visible_line(); - int first_vis_wrap = cursor.wrap_ofs; - - if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) { - cursor_set_line(first_vis_line, false, false, first_vis_wrap); - } - } + end_complex_operation(); } -/**** TEXT EDIT CORE API ****/ - -void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, int &r_end_line, int &r_end_column) { - // Save for undo. - ERR_FAIL_INDEX(p_line, text.size()); - ERR_FAIL_COND(p_char < 0); - - /* STEP 1: Remove \r from source text and separate in substrings. */ - - Vector<String> substrings = p_text.replace("\r", "").split("\n"); - - // Is this just a new empty line? - bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n"; - - /* STEP 2: Add spaces if the char is greater than the end of the line. */ - while (p_char > text[p_line].length()) { - text.set(p_line, text[p_line] + String::chr(' '), structured_text_parser(st_parser, st_args, text[p_line] + String::chr(' '))); +void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) { + // Handle selection + if (p_select) { + _pre_shift_selection(); + } else if (selection.active && !p_move_by_word) { + // If a selection is active, move caret to start of selection + set_caret_line(selection.from_line); + set_caret_column(selection.from_column); + deselect(); + return; + } else { + deselect(); } - /* STEP 3: Separate dest string in pre and post text. */ - - String preinsert_text = text[p_line].substr(0, p_char); - String postinsert_text = text[p_line].substr(p_char, text[p_line].size()); - - for (int j = 0; j < substrings.size(); j++) { - // Insert the substrings. + if (p_move_by_word) { + int cc = caret.column; - if (j == 0) { - text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j])); + if (cc == 0 && caret.line > 0) { + set_caret_line(caret.line - 1); + set_caret_column(text[caret.line].length()); } else { - text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j])); - } - - if (j == substrings.size() - 1) { - text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text)); + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < cc) { + cc = words[i].x; + break; + } + } + set_caret_column(cc); } - } - - if (shift_first_line) { - text.move_gutters(p_line, p_line + 1); - text.set_hidden(p_line + 1, text.is_hidden(p_line)); - - text.set_hidden(p_line, false); - } - - text.invalidate_cache(p_line); - - r_end_line = p_line + substrings.size() - 1; - r_end_column = text[r_end_line].length() - postinsert_text.length(); - - TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? cursor.column : 0, r_end_column); - if (dir != TextServer::DIRECTION_AUTO) { - input_direction = (TextDirection)dir; - } - - if (!text_changed_dirty && !setting_text) { - if (is_inside_tree()) { - MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); + } else { + // If the caret is at the start of the line, and not on the first line, move it up to the end of the previous line. + if (caret.column == 0) { + if (caret.line > 0) { + set_caret_line(caret.line - get_next_visible_line_offset_from(CLAMP(caret.line - 1, 0, text.size() - 1), -1)); + set_caret_column(text[caret.line].length()); + } + } else { + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() - 1); + } else { + set_caret_column(TS->shaped_text_prev_grapheme_pos(text.get_line_data(caret.line)->get_rid(), get_caret_column())); + } } - text_changed_dirty = true; } - emit_signal(SNAME("lines_edited_from"), p_line, r_end_line); -} - -String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const { - ERR_FAIL_INDEX_V(p_from_line, text.size(), String()); - ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, String()); - ERR_FAIL_INDEX_V(p_to_line, text.size(), String()); - ERR_FAIL_INDEX_V(p_to_column, text[p_to_line].length() + 1, String()); - ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'. - ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'. - String ret; - - for (int i = p_from_line; i <= p_to_line; i++) { - int begin = (i == p_from_line) ? p_from_column : 0; - int end = (i == p_to_line) ? p_to_column : text[i].length(); - - if (i > p_from_line) { - ret += "\n"; - } - ret += text[i].substr(begin, end - begin); + if (p_select) { + _post_shift_selection(); } - - return ret; } -void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { - ERR_FAIL_INDEX(p_from_line, text.size()); - ERR_FAIL_INDEX(p_from_column, text[p_from_line].length() + 1); - ERR_FAIL_INDEX(p_to_line, text.size()); - ERR_FAIL_INDEX(p_to_column, text[p_to_line].length() + 1); - ERR_FAIL_COND(p_to_line < p_from_line); // 'from > to'. - ERR_FAIL_COND(p_to_line == p_from_line && p_to_column < p_from_column); // 'from > to'. - - String pre_text = text[p_from_line].substr(0, p_from_column); - String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length()); - - for (int i = p_from_line; i < p_to_line; i++) { - text.remove(p_from_line + 1); +void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) { + // Handle selection + if (p_select) { + _pre_shift_selection(); + } else if (selection.active && !p_move_by_word) { + // If a selection is active, move caret to end of selection + set_caret_line(selection.to_line); + set_caret_column(selection.to_column); + deselect(); + return; + } else { + deselect(); } - text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text)); - //text.set_line_wrap_amount(p_from_line, -1); - text.invalidate_cache(p_from_line); + if (p_move_by_word) { + int cc = caret.column; - if (!text_changed_dirty && !setting_text) { - if (is_inside_tree()) { - MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); + if (cc == text[caret.line].length() && caret.line < text.size() - 1) { + set_caret_line(caret.line + 1); + set_caret_column(0); + } else { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > cc) { + cc = words[i].y; + break; + } + } + set_caret_column(cc); + } + } else { + // If we are at the end of the line, move the caret to the next line down. + if (caret.column == text[caret.line].length()) { + if (caret.line < text.size() - 1) { + set_caret_line(get_caret_line() + get_next_visible_line_offset_from(CLAMP(caret.line + 1, 0, text.size() - 1), 1), true, false); + set_caret_column(0); + } + } else { + if (caret_mid_grapheme_enabled) { + set_caret_column(get_caret_column() + 1); + } else { + set_caret_column(TS->shaped_text_next_grapheme_pos(text.get_line_data(caret.line)->get_rid(), get_caret_column())); + } } - text_changed_dirty = true; - } - emit_signal(SNAME("lines_edited_from"), p_to_line, p_from_line); -} - -void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) { - if (!setting_text && idle_detect->is_inside_tree()) { - idle_detect->start(); - } - - if (undo_enabled) { - _clear_redo(); - } - - int retline, retchar; - _base_insert_text(p_line, p_char, p_text, retline, retchar); - if (r_end_line) { - *r_end_line = retline; - } - if (r_end_char) { - *r_end_char = retchar; - } - - if (!undo_enabled) { - return; } - /* UNDO!! */ - TextOperation op; - op.type = TextOperation::TYPE_INSERT; - op.from_line = p_line; - op.from_column = p_char; - op.to_line = retline; - op.to_column = retchar; - op.text = p_text; - op.version = ++version; - op.chain_forward = false; - op.chain_backward = false; - - // See if it should just be set as current op. - if (current_op.type != op.type) { - op.prev_version = get_version(); - _push_current_op(); - current_op = op; - - return; // Set as current op, return. - } - // See if it can be merged. - if (current_op.to_line != p_line || current_op.to_column != p_char) { - op.prev_version = get_version(); - _push_current_op(); - current_op = op; - return; // Set as current op, return. + if (p_select) { + _post_shift_selection(); } - // Merge current op. - - current_op.text += p_text; - current_op.to_column = retchar; - current_op.to_line = retline; - current_op.version = op.version; } -void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { - if (!setting_text && idle_detect->is_inside_tree()) { - idle_detect->start(); - } - - String text; - if (undo_enabled) { - _clear_redo(); - text = _base_get_text(p_from_line, p_from_column, p_to_line, p_to_column); - } - - _base_remove_text(p_from_line, p_from_column, p_to_line, p_to_column); - - if (!undo_enabled) { - return; - } - - /* UNDO! */ - TextOperation op; - op.type = TextOperation::TYPE_REMOVE; - op.from_line = p_from_line; - op.from_column = p_from_column; - op.to_line = p_to_line; - op.to_column = p_to_column; - op.text = text; - op.version = ++version; - op.chain_forward = false; - op.chain_backward = false; - - // See if it should just be set as current op. - if (current_op.type != op.type) { - op.prev_version = get_version(); - _push_current_op(); - current_op = op; - return; // Set as current op, return. - } - // See if it can be merged. - if (current_op.from_line == p_to_line && current_op.from_column == p_to_column) { - // Backspace or similar. - current_op.text = text + current_op.text; - current_op.from_line = p_from_line; - current_op.from_column = p_from_column; - return; // Update current op. +void TextEdit::_move_caret_up(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); } - op.prev_version = get_version(); - _push_current_op(); - current_op = op; -} - -int TextEdit::get_char_count() { - int totalsize = 0; - - for (int i = 0; i < text.size(); i++) { - if (i > 0) { - totalsize++; // Include \n. + int cur_wrap_index = get_caret_wrap_index(); + if (cur_wrap_index > 0) { + set_caret_line(caret.line, true, false, cur_wrap_index - 1); + } else if (caret.line == 0) { + set_caret_column(0); + } else { + int new_line = caret.line - get_next_visible_line_offset_from(caret.line - 1, -1); + if (is_line_wrapped(new_line)) { + set_caret_line(new_line, true, false, get_line_wrap_count(new_line)); + } else { + set_caret_line(new_line, true, false); } - totalsize += text[i].length(); } - return totalsize; // Omit last \n. -} - -Size2 TextEdit::get_minimum_size() const { - return cache.style_normal->get_minimum_size(); -} - -int TextEdit::_get_control_height() const { - int control_height = get_size().height; - control_height -= cache.style_normal->get_minimum_size().height; - if (h_scroll->is_visible_in_tree()) { - control_height -= h_scroll->get_size().height; + if (p_select) { + _post_shift_selection(); } - return control_height; } -int TextEdit::_get_menu_action_accelerator(const String &p_action) { - const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); - if (!events) { - return 0; - } - - // Use first event in the list for the accelerator. - const List<Ref<InputEvent>>::Element *first_event = events->front(); - if (!first_event) { - return 0; - } - - const Ref<InputEventKey> event = first_event->get(); - if (event.is_null()) { - return 0; - } - - // Use physical keycode if non-zero - if (event->get_physical_keycode() != 0) { - return event->get_physical_keycode_with_modifiers(); +void TextEdit::_move_caret_down(bool p_select) { + if (p_select) { + _pre_shift_selection(); } else { - return event->get_keycode_with_modifiers(); - } -} - -int TextEdit::get_visible_rows() const { - return _get_control_height() / get_row_height(); -} - -int TextEdit::_get_minimap_visible_rows() const { - return _get_control_height() / (minimap_char_size.y + minimap_line_spacing); -} - -int TextEdit::get_total_visible_rows() const { - // Returns the total amount of rows we need in the editor. - // This skips hidden lines and counts each wrapping of a line. - if (!is_hiding_enabled() && !is_wrap_enabled()) { - return text.size(); - } - - int total_rows = 0; - for (int i = 0; i < text.size(); i++) { - if (!text.is_hidden(i)) { - total_rows++; - total_rows += times_line_wraps(i); - } + deselect(); } - return total_rows; -} -void TextEdit::_update_wrap_at(bool p_force) { - int new_wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding; - if (draw_minimap) { - new_wrap_at -= minimap_width; - } - if (v_scroll->is_visible_in_tree()) { - new_wrap_at -= v_scroll->get_combined_minimum_size().width; + int cur_wrap_index = get_caret_wrap_index(); + if (cur_wrap_index < get_line_wrap_count(caret.line)) { + set_caret_line(caret.line, true, false, cur_wrap_index + 1); + } else if (caret.line == get_last_unhidden_line()) { + set_caret_column(text[caret.line].length()); + } else { + int new_line = caret.line + get_next_visible_line_offset_from(CLAMP(caret.line + 1, 0, text.size() - 1), 1); + set_caret_line(new_line, true, false, 0); } - new_wrap_at -= wrap_right_offset; // Give it a little more space. - if ((wrap_at != new_wrap_at) || p_force) { - wrap_at = new_wrap_at; - if (wrap_enabled) { - text.set_width(wrap_at); - } else { - text.set_width(-1); - } - text.invalidate_all_lines(); + if (p_select) { + _post_shift_selection(); } - - update_cursor_wrap_offset(); } -void TextEdit::adjust_viewport_to_cursor() { - // Make sure cursor is visible on the screen. - scrolling = false; - minimap_clicked = false; - - int cur_line = cursor.line; - int cur_wrap = get_cursor_wrap_index(); - - int first_vis_line = get_first_visible_line(); - int first_vis_wrap = cursor.wrap_ofs; - int last_vis_line = get_last_full_visible_line(); - int last_vis_wrap = get_last_full_visible_line_wrap_index(); - - if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) { - // Cursor is above screen. - set_line_as_first_visible(cur_line, cur_wrap); - } else if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) { - // Cursor is below screen. - set_line_as_last_visible(cur_line, cur_wrap); +void TextEdit::_move_caret_to_line_start(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); } - int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width; - if (v_scroll->is_visible_in_tree()) { - visible_width -= v_scroll->get_combined_minimum_size().width; + // Move caret column to start of wrapped row and then to start of text. + Vector<String> rows = get_line_wrapped_text(caret.line); + int wi = get_caret_wrap_index(); + int row_start_col = 0; + for (int i = 0; i < wi; i++) { + row_start_col += rows[i].length(); } - visible_width -= 20; // Give it a little more space. - - if (!is_wrap_enabled()) { - // Adjust x offset. - Vector2i cursor_pos; - - // Get position of the start of caret. - if (ime_text.length() != 0 && ime_selection.x != 0) { - cursor_pos.x = get_column_x_offset_for_line(cursor.column + ime_selection.x, cursor.line); - } else { - cursor_pos.x = get_column_x_offset_for_line(cursor.column, cursor.line); - } - - // Get position of the end of caret. - if (ime_text.length() != 0) { - if (ime_selection.y != 0) { - cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_selection.x + ime_selection.y, cursor.line); - } else { - cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_text.size(), cursor.line); + if (caret.column == row_start_col || wi == 0) { + // Compute whitespace symbols sequence length. + int current_line_whitespace_len = 0; + while (current_line_whitespace_len < text[caret.line].length()) { + char32_t c = text[caret.line][current_line_whitespace_len]; + if (c != '\t' && c != ' ') { + break; } - } else { - cursor_pos.y = cursor_pos.x; - } - - if (MAX(cursor_pos.x, cursor_pos.y) > (cursor.x_ofs + visible_width)) { - cursor.x_ofs = MAX(cursor_pos.x, cursor_pos.y) - visible_width + 1; + current_line_whitespace_len++; } - if (MIN(cursor_pos.x, cursor_pos.y) < cursor.x_ofs) { - cursor.x_ofs = MIN(cursor_pos.x, cursor_pos.y); + if (get_caret_column() == current_line_whitespace_len) { + set_caret_column(0); + } else { + set_caret_column(current_line_whitespace_len); } } else { - cursor.x_ofs = 0; + set_caret_column(row_start_col); } - h_scroll->set_value(cursor.x_ofs); - - update(); -} - -void TextEdit::center_viewport_to_cursor() { - // Move viewport so the cursor is in the center of the screen. - scrolling = false; - minimap_clicked = false; - set_line_as_center_visible(cursor.line, get_cursor_wrap_index()); - int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width; - if (v_scroll->is_visible_in_tree()) { - visible_width -= v_scroll->get_combined_minimum_size().width; + if (p_select) { + _post_shift_selection(); } - visible_width -= 20; // Give it a little more space. - - if (is_wrap_enabled()) { - // Center x offset. - - Vector2i cursor_pos; - - // Get position of the start of caret. - if (ime_text.length() != 0 && ime_selection.x != 0) { - cursor_pos.x = get_column_x_offset_for_line(cursor.column + ime_selection.x, cursor.line); - } else { - cursor_pos.x = get_column_x_offset_for_line(cursor.column, cursor.line); - } - - // Get position of the end of caret. - if (ime_text.length() != 0) { - if (ime_selection.y != 0) { - cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_selection.x + ime_selection.y, cursor.line); - } else { - cursor_pos.y = get_column_x_offset_for_line(cursor.column + ime_text.size(), cursor.line); - } - } else { - cursor_pos.y = cursor_pos.x; - } - - if (MAX(cursor_pos.x, cursor_pos.y) > (cursor.x_ofs + visible_width)) { - cursor.x_ofs = MAX(cursor_pos.x, cursor_pos.y) - visible_width + 1; - } +} - if (MIN(cursor_pos.x, cursor_pos.y) < cursor.x_ofs) { - cursor.x_ofs = MIN(cursor_pos.x, cursor_pos.y); - } +void TextEdit::_move_caret_to_line_end(bool p_select) { + if (p_select) { + _pre_shift_selection(); } else { - cursor.x_ofs = 0; + deselect(); } - h_scroll->set_value(cursor.x_ofs); - - update(); -} -void TextEdit::update_cursor_wrap_offset() { - int first_vis_line = get_first_visible_line(); - if (line_wraps(first_vis_line)) { - cursor.wrap_ofs = MIN(cursor.wrap_ofs, times_line_wraps(first_vis_line)); + // Move caret column to end of wrapped row and then to end of text. + Vector<String> rows = get_line_wrapped_text(caret.line); + int wi = get_caret_wrap_index(); + int row_end_col = -1; + for (int i = 0; i < wi + 1; i++) { + row_end_col += rows[i].length(); + } + if (wi == rows.size() - 1 || caret.column == row_end_col) { + set_caret_column(text[caret.line].length()); } else { - cursor.wrap_ofs = 0; + set_caret_column(row_end_col); } - set_line_as_first_visible(cursor.line_ofs, cursor.wrap_ofs); -} -bool TextEdit::line_wraps(int line) const { - ERR_FAIL_INDEX_V(line, text.size(), 0); - if (!is_wrap_enabled()) { - return false; + if (p_select) { + _post_shift_selection(); } - return text.get_line_wrap_amount(line) > 0; } -int TextEdit::times_line_wraps(int line) const { - ERR_FAIL_INDEX_V(line, text.size(), 0); - - if (!line_wraps(line)) { - return 0; +void TextEdit::_move_caret_page_up(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); } - return text.get_line_wrap_amount(line); -} - -Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>()); + Point2i next_line = get_next_visible_line_index_offset_from(caret.line, get_caret_wrap_index(), -get_visible_line_count()); + int n_line = caret.line - next_line.x + 1; + set_caret_line(n_line, true, false, next_line.y); - Vector<String> lines; - if (!line_wraps(p_line)) { - lines.push_back(text[p_line]); - return lines; + if (p_select) { + _post_shift_selection(); } +} - const String &line_text = text[p_line]; - Vector<Vector2i> line_ranges = text.get_line_wrap_ranges(p_line); - for (int i = 0; i < line_ranges.size(); i++) { - lines.push_back(line_text.substr(line_ranges[i].x, line_ranges[i].y - line_ranges[i].x)); +void TextEdit::_move_caret_page_down(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); } - return lines; -} + Point2i next_line = get_next_visible_line_index_offset_from(caret.line, get_caret_wrap_index(), get_visible_line_count()); + int n_line = caret.line + next_line.x - 1; + set_caret_line(n_line, true, false, next_line.y); -int TextEdit::get_cursor_wrap_index() const { - return get_line_wrap_index_at_col(cursor.line, cursor.column); + if (p_select) { + _post_shift_selection(); + } } -int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); - - if (!line_wraps(p_line)) { - return 0; +void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { + if (!editable) { + return; } - // Loop through wraps in the line text until we get to the column. - int wrap_index = 0; - int col = 0; - Vector<String> rows = get_wrap_rows_text(p_line); - for (int i = 0; i < rows.size(); i++) { - wrap_index = i; - String s = rows[wrap_index]; - col += s.length(); - if (col > p_column) { - break; - } + if (has_selection() || (!p_all_to_left && !p_word)) { + backspace(); + return; } - return wrap_index; -} - -void TextEdit::set_mid_grapheme_caret_enabled(const bool p_enabled) { - mid_grapheme_caret_enabled = p_enabled; -} - -bool TextEdit::get_mid_grapheme_caret_enabled() const { - return mid_grapheme_caret_enabled; -} -void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { - if (p_col < 0) { - p_col = 0; + if (p_all_to_left) { + int caret_current_column = caret.column; + caret.column = 0; + _remove_text(caret.line, 0, caret.line, caret_current_column); + return; } - cursor.column = p_col; - if (cursor.column > get_line(cursor.line).length()) { - cursor.column = get_line(cursor.line).length(); - } + if (p_word) { + int line = caret.line; + int column = caret.column; - cursor.last_fit_x = get_column_x_offset_for_line(cursor.column, cursor.line); + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = words.size() - 1; i >= 0; i--) { + if (words[i].x < column) { + column = words[i].x; + break; + } + } - if (p_adjust_viewport) { - adjust_viewport_to_cursor(); - } + _remove_text(line, column, caret.line, caret.column); - if (!cursor_changed_dirty) { - if (is_inside_tree()) { - MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit"); - } - cursor_changed_dirty = true; + set_caret_line(line, false); + set_caret_column(column); + return; } } -void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden, int p_wrap_index) { - if (setting_row) { +void TextEdit::_delete(bool p_word, bool p_all_to_right) { + if (!editable) { return; } - setting_row = true; - if (p_row < 0) { - p_row = 0; - } - - if (p_row >= text.size()) { - p_row = text.size() - 1; - } - - if (!p_can_be_hidden) { - if (is_line_hidden(CLAMP(p_row, 0, text.size() - 1))) { - int move_down = num_lines_from(p_row, 1) - 1; - if (p_row + move_down <= text.size() - 1 && !is_line_hidden(p_row + move_down)) { - p_row += move_down; - } else { - int move_up = num_lines_from(p_row, -1) - 1; - if (p_row - move_up > 0 && !is_line_hidden(p_row - move_up)) { - p_row -= move_up; - } else { - WARN_PRINT(("Cursor set to hidden line " + itos(p_row) + " and there are no nonhidden lines.")); - } - } - } + if (has_selection()) { + delete_selection(); + return; } - cursor.line = p_row; + int curline_len = text[caret.line].length(); - int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index); - if (n_col != 0 && is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) { - Vector<String> rows = get_wrap_rows_text(p_row); - int row_end_col = 0; - for (int i = 0; i < p_wrap_index + 1; i++) { - row_end_col += rows[i].length(); - } - if (n_col >= row_end_col) { - n_col -= 1; - } + if (caret.line == text.size() - 1 && caret.column == curline_len) { + return; // Last line, last column: Nothing to do. } - cursor.column = n_col; - if (p_adjust_viewport) { - adjust_viewport_to_cursor(); - } + int next_line = caret.column < curline_len ? caret.line : caret.line + 1; + int next_column; - setting_row = false; + if (p_all_to_right) { + // Delete everything to right of caret + next_column = curline_len; + next_line = caret.line; + } else if (p_word && caret.column < curline_len - 1) { + // Delete next word to right of caret + int line = caret.line; + int column = caret.column; - if (!cursor_changed_dirty) { - if (is_inside_tree()) { - MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit"); + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].y > column) { + column = words[i].y; + break; + } } - cursor_changed_dirty = true; - } -} - -Point2 TextEdit::get_caret_draw_pos() const { - return cursor.draw_pos; -} - -bool TextEdit::is_caret_visible() const { - return cursor.visible; -} - -int TextEdit::cursor_get_column() const { - return cursor.column; -} - -int TextEdit::cursor_get_line() const { - return cursor.line; -} -bool TextEdit::cursor_get_blink_enabled() const { - return caret_blink_enabled; -} - -void TextEdit::cursor_set_blink_enabled(const bool p_enabled) { - caret_blink_enabled = p_enabled; - - if (has_focus()) { - if (p_enabled) { - caret_blink_timer->start(); + next_line = line; + next_column = column; + } else { + // Delete one character + next_column = caret.column < curline_len ? (caret.column + 1) : 0; + if (caret_mid_grapheme_enabled) { + next_column = caret.column < curline_len ? (caret.column + 1) : 0; } else { - caret_blink_timer->stop(); + next_column = caret.column < curline_len ? TS->shaped_text_next_grapheme_pos(text.get_line_data(caret.line)->get_rid(), (caret.column)) : 0; } } - draw_caret = true; -} - -float TextEdit::cursor_get_blink_speed() const { - return caret_blink_timer->get_wait_time(); -} - -void TextEdit::cursor_set_blink_speed(const float p_speed) { - ERR_FAIL_COND(p_speed <= 0); - caret_blink_timer->set_wait_time(p_speed); -} - -void TextEdit::cursor_set_block_mode(const bool p_enable) { - block_caret = p_enable; + _remove_text(caret.line, caret.column, next_line, next_column); update(); } -bool TextEdit::cursor_is_block_mode() const { - return block_caret; -} - -void TextEdit::set_right_click_moves_caret(bool p_enable) { - right_click_moves_caret = p_enable; -} +void TextEdit::_move_caret_document_start(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); + } -bool TextEdit::is_right_click_moving_caret() const { - return right_click_moves_caret; -} + set_caret_line(0); + set_caret_column(0); -TextEdit::SelectionMode TextEdit::get_selection_mode() const { - return selection.selecting_mode; + if (p_select) { + _post_shift_selection(); + } } -void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column) { - selection.selecting_mode = p_mode; - if (p_line >= 0) { - ERR_FAIL_INDEX(p_line, text.size()); - selection.selecting_line = p_line; +void TextEdit::_move_caret_document_end(bool p_select) { + if (p_select) { + _pre_shift_selection(); + } else { + deselect(); } - if (p_column >= 0) { - ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length()); - selection.selecting_column = p_column; + + set_caret_line(get_last_unhidden_line(), true, false, 9999); + set_caret_column(text[caret.line].length()); + + if (p_select) { + _post_shift_selection(); } } -int TextEdit::get_selection_line() const { - return selection.selecting_line; -}; - -int TextEdit::get_selection_column() const { - return selection.selecting_column; -}; +void TextEdit::_update_caches() { + /* Internal API for CodeEdit. */ + brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color"), SNAME("CodeEdit")); + code_folding_color = get_theme_color(SNAME("code_folding_color"), SNAME("CodeEdit")); + folded_eol_icon = get_theme_icon(SNAME("folded_eol_icon"), SNAME("CodeEdit")); -void TextEdit::_v_scroll_input() { - scrolling = false; - minimap_clicked = false; -} + /* Search */ + search_result_color = get_theme_color(SNAME("search_result_color")); + search_result_border_color = get_theme_color(SNAME("search_result_border_color")); -void TextEdit::_scroll_moved(double p_to_val) { - if (updating_scrolls) { - return; - } + /* Caret */ + caret_color = get_theme_color(SNAME("caret_color")); + caret_background_color = get_theme_color(SNAME("caret_background_color")); - if (h_scroll->is_visible_in_tree()) { - cursor.x_ofs = h_scroll->get_value(); - } - if (v_scroll->is_visible_in_tree()) { - // Set line ofs and wrap ofs. - int v_scroll_i = floor(get_v_scroll()); - int sc = 0; - int n_line; - for (n_line = 0; n_line < text.size(); n_line++) { - if (!is_line_hidden(n_line)) { - sc++; - sc += times_line_wraps(n_line); - if (sc > v_scroll_i) { - break; - } - } - } - n_line = MIN(n_line, text.size() - 1); - int line_wrap_amount = times_line_wraps(n_line); - int wi = line_wrap_amount - (sc - v_scroll_i - 1); - wi = CLAMP(wi, 0, line_wrap_amount); + /* Selection */ + font_selected_color = get_theme_color(SNAME("font_selected_color")); + selection_color = get_theme_color(SNAME("selection_color")); - cursor.line_ofs = n_line; - cursor.wrap_ofs = wi; - } - update(); -} + /* Visual. */ + style_normal = get_theme_stylebox(SNAME("normal")); + style_focus = get_theme_stylebox(SNAME("focus")); + style_readonly = get_theme_stylebox(SNAME("read_only")); -int TextEdit::get_row_height() const { - int height = cache.font->get_height(cache.font_size); - for (int i = 0; i < text.size(); i++) { - for (int j = 0; j <= text.get_line_wrap_amount(i); j++) { - height = MAX(height, text.get_line_height(i, j)); - } - } - return height + cache.line_spacing; -} + tab_icon = get_theme_icon(SNAME("tab")); + space_icon = get_theme_icon(SNAME("space")); -int TextEdit::get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); - p_wrap_index = MIN(p_wrap_index, text.get_line_data(p_line)->get_line_count() - 1); + font = get_theme_font(SNAME("font")); + font_size = get_theme_font_size(SNAME("font_size")); + font_color = get_theme_color(SNAME("font_color")); + font_readonly_color = get_theme_color(SNAME("font_readonly_color")); - RID text_rid = text.get_line_data(p_line)->get_line_rid(p_wrap_index); - if (is_layout_rtl()) { - p_px = TS->shaped_text_get_size(text_rid).x - p_px; - } - return TS->shaped_text_hit_test_position(text_rid, p_px); -} + outline_size = get_theme_constant(SNAME("outline_size")); + outline_color = get_theme_color(SNAME("font_outline_color")); -int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); + line_spacing = get_theme_constant(SNAME("line_spacing")); - int row = 0; - Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line); - for (int i = 0; i < rows2.size(); i++) { - if ((p_char >= rows2[i].x) && (p_char < rows2[i].y)) { - row = i; - break; - } - } + background_color = get_theme_color(SNAME("background_color")); + current_line_color = get_theme_color(SNAME("current_line_color")); + word_highlighted_color = get_theme_color(SNAME("word_highlighted_color")); - Rect2 l_caret, t_caret; - TextServer::Direction l_dir, t_dir; - RID text_rid = text.get_line_data(p_line)->get_line_rid(row); - TS->shaped_text_get_carets(text_rid, cursor.column, l_caret, l_dir, t_caret, t_dir); - if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { - return l_caret.position.x; + /* Text properties. */ + TextServer::Direction dir; + if (text_direction == Control::TEXT_DIRECTION_INHERITED) { + dir = is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; } else { - return t_caret.position.x; + dir = (TextServer::Direction)text_direction; } -} + text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); + text.set_font_features(opentype_features); + text.set_draw_control_chars(draw_control_chars); + text.set_font(font); + text.set_font_size(font_size); + text.invalidate_all(); -void TextEdit::insert_text_at_cursor(const String &p_text) { - if (selection.active) { - delete_selection(); + /* Syntax highlighting. */ + if (syntax_highlighter.is_valid()) { + syntax_highlighter->set_text_edit(this); } +} - int new_column, new_line; - _insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column); - _update_scrollbars(); +/* General overrides. */ +Size2 TextEdit::get_minimum_size() const { + return style_normal->get_minimum_size(); +} - cursor_set_line(new_line, false); - cursor_set_column(new_column); - update(); +bool TextEdit::is_text_field() const { + return true; } Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { - int row, col; - _get_mouse_pos(p_pos, row, col); + Point2i pos = get_line_column_at_pos(p_pos); + int row = pos.y; - int left_margin = cache.style_normal->get_margin(SIDE_LEFT); + int left_margin = style_normal->get_margin(SIDE_LEFT); int gutter = left_margin + gutters_width; if (p_pos.x < gutter) { for (int i = 0; i < gutters.size(); i++) { @@ -3519,75 +2241,59 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { return CURSOR_ARROW; } - int xmargin_end = get_size().width - cache.style_normal->get_margin(SIDE_RIGHT); + int xmargin_end = get_size().width - style_normal->get_margin(SIDE_RIGHT); if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) { return CURSOR_ARROW; } return get_default_cursor_shape(); } -void TextEdit::set_text(String p_text) { - setting_text = true; - if (!undo_enabled) { - _clear(); - insert_text_at_cursor(p_text); +String TextEdit::get_tooltip(const Point2 &p_pos) const { + if (!tooltip_obj) { + return Control::get_tooltip(p_pos); } + Point2i pos = get_line_column_at_pos(p_pos); + int row = pos.y; + int col = pos.x; - if (undo_enabled) { - cursor_set_line(0); - cursor_set_column(0); - - begin_complex_operation(); - _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0)); - insert_text_at_cursor(p_text); - end_complex_operation(); - selection.active = false; + String s = text[row]; + if (s.length() == 0) { + return Control::get_tooltip(p_pos); } + int beg, end; + if (select_word(s, col, beg, end)) { + String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud); - cursor_set_line(0); - cursor_set_column(0); - - update(); - setting_text = false; -} - -String TextEdit::get_text() { - String longthing; - int len = text.size(); - for (int i = 0; i < len; i++) { - longthing += text[i]; - if (i != len - 1) { - longthing += "\n"; - } + return tt; } - return longthing; + return Control::get_tooltip(p_pos); } -void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { - if (st_parser != p_parser) { - st_parser = p_parser; - for (int i = 0; i < text.size(); i++) { - text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); - } - update(); - } +void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) { + tooltip_obj = p_obj; + tooltip_func = p_function; + tooltip_ud = p_udata; } -Control::StructuredTextParser TextEdit::get_structured_text_bidi_override() const { - return st_parser; +/* Text */ +// Text properties. +bool TextEdit::has_ime_text() const { + return !ime_text.is_empty(); } -void TextEdit::set_structured_text_bidi_override_options(Array p_args) { - st_args = p_args; - for (int i = 0; i < text.size(); i++) { - text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); +void TextEdit::set_editable(const bool p_editable) { + if (editable == p_editable) { + return; } + + editable = p_editable; + update(); } -Array TextEdit::get_structured_text_bidi_override_options() const { - return st_args; +bool TextEdit::is_editable() const { + return editable; } void TextEdit::set_text_direction(Control::TextDirection p_text_direction) { @@ -3620,13 +2326,6 @@ Control::TextDirection TextEdit::get_text_direction() const { return text_direction; } -void TextEdit::clear_opentype_features() { - opentype_features.clear(); - text.set_font_features(opentype_features); - text.invalidate_all(); - update(); -} - void TextEdit::set_opentype_feature(const String &p_name, int p_value) { int32_t tag = TS->name_to_tag(p_name); if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { @@ -3645,6 +2344,13 @@ int TextEdit::get_opentype_feature(const String &p_name) const { return opentype_features[tag]; } +void TextEdit::clear_opentype_features() { + opentype_features.clear(); + text.set_font_features(opentype_features); + text.invalidate_all(); + update(); +} + void TextEdit::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -3664,630 +2370,667 @@ String TextEdit::get_language() const { return language; } -void TextEdit::set_draw_control_chars(bool p_draw_control_chars) { - if (draw_control_chars != p_draw_control_chars) { - draw_control_chars = p_draw_control_chars; - if (menu && menu->get_item_index(MENU_DISPLAY_UCC) >= 0) { - menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); +void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + for (int i = 0; i < text.size(); i++) { + text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); } - text.set_draw_control_chars(draw_control_chars); - text.invalidate_all(); update(); } } -bool TextEdit::get_draw_control_chars() const { - return draw_control_chars; +Control::StructuredTextParser TextEdit::get_structured_text_bidi_override() const { + return st_parser; } -String TextEdit::get_line(int line) const { - if (line < 0 || line >= text.size()) { - return ""; +void TextEdit::set_structured_text_bidi_override_options(Array p_args) { + st_args = p_args; + for (int i = 0; i < text.size(); i++) { + text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i])); } - - return text[line]; -}; - -bool TextEdit::has_ime_text() const { - return !ime_text.is_empty(); + update(); } -void TextEdit::_clear() { - clear_undo_history(); - text.clear(); - cursor.column = 0; - cursor.line = 0; - cursor.x_ofs = 0; - cursor.line_ofs = 0; - cursor.wrap_ofs = 0; - cursor.last_fit_x = 0; - selection.active = false; +Array TextEdit::get_structured_text_bidi_override_options() const { + return st_args; } -void TextEdit::clear() { - setting_text = true; - _clear(); - setting_text = false; -}; - -void TextEdit::set_readonly(bool p_readonly) { - if (readonly == p_readonly) { +void TextEdit::set_tab_size(const int p_size) { + ERR_FAIL_COND_MSG(p_size <= 0, "Tab size must be greater than 0."); + if (p_size == text.get_tab_size()) { return; } - - readonly = p_readonly; - + text.set_tab_size(p_size); + text.invalidate_all_lines(); update(); } -bool TextEdit::is_readonly() const { - return readonly; +int TextEdit::get_tab_size() const { + return text.get_tab_size(); } -void TextEdit::set_wrap_enabled(bool p_wrap_enabled) { - if (wrap_enabled != p_wrap_enabled) { - wrap_enabled = p_wrap_enabled; - _update_wrap_at(true); - } +// User controls +void TextEdit::set_overtype_mode_enabled(const bool p_enabled) { + overtype_mode = p_enabled; + update(); } -bool TextEdit::is_wrap_enabled() const { - return wrap_enabled; +bool TextEdit::is_overtype_mode_enabled() const { + return overtype_mode; } -void TextEdit::_reset_caret_blink_timer() { - if (caret_blink_enabled) { - draw_caret = true; - if (has_focus()) { - caret_blink_timer->stop(); - caret_blink_timer->start(); - update(); - } - } +void TextEdit::set_context_menu_enabled(bool p_enable) { + context_menu_enabled = p_enable; } -void TextEdit::_toggle_draw_caret() { - draw_caret = !draw_caret; - if (is_visible_in_tree() && has_focus() && window_has_focus) { - update(); - } +bool TextEdit::is_context_menu_enabled() const { + return context_menu_enabled; } -void TextEdit::_update_caches() { - cache.style_normal = get_theme_stylebox(SNAME("normal")); - cache.style_focus = get_theme_stylebox(SNAME("focus")); - cache.style_readonly = get_theme_stylebox(SNAME("read_only")); - cache.font = get_theme_font(SNAME("font")); - cache.font_size = get_theme_font_size(SNAME("font_size")); - cache.outline_color = get_theme_color(SNAME("font_outline_color")); - cache.outline_size = get_theme_constant(SNAME("outline_size")); - cache.caret_color = get_theme_color(SNAME("caret_color")); - cache.caret_background_color = get_theme_color(SNAME("caret_background_color")); - cache.font_color = get_theme_color(SNAME("font_color")); - cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); - cache.font_readonly_color = get_theme_color(SNAME("font_readonly_color")); - cache.selection_color = get_theme_color(SNAME("selection_color")); - cache.current_line_color = get_theme_color(SNAME("current_line_color")); - cache.code_folding_color = get_theme_color(SNAME("code_folding_color"), SNAME("CodeEdit")); - cache.brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color"), SNAME("CodeEdit")); - cache.word_highlighted_color = get_theme_color(SNAME("word_highlighted_color")); - cache.search_result_color = get_theme_color(SNAME("search_result_color")); - cache.search_result_border_color = get_theme_color(SNAME("search_result_border_color")); - cache.background_color = get_theme_color(SNAME("background_color")); -#ifdef TOOLS_ENABLED - cache.line_spacing = get_theme_constant(SNAME("line_spacing")) * EDSCALE; -#else - cache.line_spacing = get_theme_constant(SNAME("line_spacing")); -#endif - cache.tab_icon = get_theme_icon(SNAME("tab")); - cache.space_icon = get_theme_icon(SNAME("space")); - cache.folded_eol_icon = get_theme_icon(SNAME("folded_eol_icon"), SNAME("CodeEdit")); - - TextServer::Direction dir; - if (text_direction == Control::TEXT_DIRECTION_INHERITED) { - dir = is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR; - } else { - dir = (TextServer::Direction)text_direction; - } - text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); - text.set_font_features(opentype_features); - text.set_draw_control_chars(draw_control_chars); - text.set_font(cache.font); - text.set_font_size(cache.font_size); - text.invalidate_all(); - - if (syntax_highlighter.is_valid()) { - syntax_highlighter->set_text_edit(this); - } +void TextEdit::set_shortcut_keys_enabled(bool p_enabled) { + shortcut_keys_enabled = p_enabled; } -/* Syntax Highlighting. */ -Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() { - return syntax_highlighter; +bool TextEdit::is_shortcut_keys_enabled() const { + return shortcut_keys_enabled; } -void TextEdit::set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter) { - syntax_highlighter = p_syntax_highlighter; - if (syntax_highlighter.is_valid()) { - syntax_highlighter->set_text_edit(this); - } - update(); +void TextEdit::set_virtual_keyboard_enabled(bool p_enable) { + virtual_keyboard_enabled = p_enable; } -/* Gutters. */ -void TextEdit::_update_gutter_width() { - gutters_width = 0; - for (int i = 0; i < gutters.size(); i++) { - if (gutters[i].draw) { - gutters_width += gutters[i].width; - } - } - if (gutters_width > 0) { - gutter_padding = 2; - } - update(); +bool TextEdit::is_virtual_keyboard_enabled() const { + return virtual_keyboard_enabled; } -void TextEdit::add_gutter(int p_at) { - if (p_at < 0 || p_at > gutters.size()) { - gutters.push_back(GutterInfo()); - } else { - gutters.insert(p_at, GutterInfo()); - } - - for (int i = 0; i < text.size() + 1; i++) { - text.add_gutter(p_at); - } - emit_signal(SNAME("gutter_added")); - update(); +// Text manipulation +void TextEdit::clear() { + setting_text = true; + _clear(); + setting_text = false; } -void TextEdit::remove_gutter(int p_gutter) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - - gutters.remove(p_gutter); +void TextEdit::_clear() { + clear_undo_history(); + text.clear(); + caret.column = 0; + caret.line = 0; + caret.x_ofs = 0; + caret.line_ofs = 0; + caret.wrap_ofs = 0; + caret.last_fit_x = 0; + selection.active = false; +} - for (int i = 0; i < text.size() + 1; i++) { - text.remove_gutter(p_gutter); +void TextEdit::set_text(const String &p_text) { + setting_text = true; + if (!undo_enabled) { + _clear(); + insert_text_at_caret(p_text); } - emit_signal(SNAME("gutter_removed")); - update(); -} -int TextEdit::get_gutter_count() const { - return gutters.size(); -} + if (undo_enabled) { + set_caret_line(0); + set_caret_column(0); -void TextEdit::set_gutter_name(int p_gutter, const String &p_name) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.write[p_gutter].name = p_name; -} + begin_complex_operation(); + _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0)); + insert_text_at_caret(p_text); + end_complex_operation(); + selection.active = false; + } -String TextEdit::get_gutter_name(int p_gutter) const { - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), ""); - return gutters[p_gutter].name; -} + set_caret_line(0); + set_caret_column(0); -void TextEdit::set_gutter_type(int p_gutter, GutterType p_type) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.write[p_gutter].type = p_type; update(); + setting_text = false; } -TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const { - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), GUTTER_TYPE_STRING); - return gutters[p_gutter].type; +String TextEdit::get_text() const { + String longthing; + int len = text.size(); + for (int i = 0; i < len; i++) { + longthing += text[i]; + if (i != len - 1) { + longthing += "\n"; + } + } + return longthing; } -void TextEdit::set_gutter_width(int p_gutter, int p_width) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.write[p_gutter].width = p_width; - _update_gutter_width(); +int TextEdit::get_line_count() const { + return text.size(); } -int TextEdit::get_gutter_width(int p_gutter) const { - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), -1); - return gutters[p_gutter].width; +void TextEdit::set_line(int p_line, const String &p_new_text) { + if (p_line < 0 || p_line >= text.size()) { + return; + } + _remove_text(p_line, 0, p_line, text[p_line].length()); + _insert_text(p_line, 0, p_new_text); + if (caret.line == p_line) { + caret.column = MIN(caret.column, p_new_text.length()); + } + if (has_selection() && p_line == selection.to_line && selection.to_column > text[p_line].length()) { + selection.to_column = text[p_line].length(); + } } -int TextEdit::get_total_gutter_width() const { - return gutters_width + gutter_padding; +String TextEdit::get_line(int p_line) const { + if (p_line < 0 || p_line >= text.size()) { + return ""; + } + return text[p_line]; } -void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.write[p_gutter].draw = p_draw; - _update_gutter_width(); -} +int TextEdit::get_line_width(int p_line, int p_wrap_index) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + ERR_FAIL_COND_V(p_wrap_index > get_line_wrap_count(p_line), 0); -bool TextEdit::is_gutter_drawn(int p_gutter) const { - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); - return gutters[p_gutter].draw; + return text.get_line_width(p_line, p_wrap_index); } -void TextEdit::set_gutter_clickable(int p_gutter, bool p_clickable) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.write[p_gutter].clickable = p_clickable; - update(); +int TextEdit::get_line_height() const { + int height = font->get_height(font_size); + for (int i = 0; i < text.size(); i++) { + for (int j = 0; j <= text.get_line_wrap_amount(i); j++) { + height = MAX(height, text.get_line_height(i, j)); + } + } + return height + line_spacing; } -bool TextEdit::is_gutter_clickable(int p_gutter) const { - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); - return gutters[p_gutter].clickable; -} +int TextEdit::get_indent_level(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); -void TextEdit::set_gutter_overwritable(int p_gutter, bool p_overwritable) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - gutters.write[p_gutter].overwritable = p_overwritable; + int tab_count = 0; + int whitespace_count = 0; + int line_length = text[p_line].size(); + for (int i = 0; i < line_length - 1; i++) { + if (text[p_line][i] == '\t') { + tab_count++; + } else if (text[p_line][i] == ' ') { + whitespace_count++; + } else { + break; + } + } + return tab_count * text.get_tab_size() + whitespace_count; } -bool TextEdit::is_gutter_overwritable(int p_gutter) const { - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); - return gutters[p_gutter].overwritable; +int TextEdit::get_first_non_whitespace_column(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + + int col = 0; + while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) { + col++; + } + return col; } -void TextEdit::merge_gutters(int p_from_line, int p_to_line) { +void TextEdit::swap_lines(int p_from_line, int p_to_line) { ERR_FAIL_INDEX(p_from_line, text.size()); ERR_FAIL_INDEX(p_to_line, text.size()); - if (p_from_line == p_to_line) { - return; - } - for (int i = 0; i < gutters.size(); i++) { - if (!gutters[i].overwritable) { - continue; - } - - if (text.get_line_gutter_text(p_from_line, i) != "") { - text.set_line_gutter_text(p_to_line, i, text.get_line_gutter_text(p_from_line, i)); - text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i)); - } - - if (text.get_line_gutter_icon(p_from_line, i).is_valid()) { - text.set_line_gutter_icon(p_to_line, i, text.get_line_gutter_icon(p_from_line, i)); - text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i)); - } + String tmp = get_line(p_from_line); + String tmp2 = get_line(p_to_line); + set_line(p_to_line, tmp); + set_line(p_from_line, tmp2); +} - if (text.get_line_gutter_metadata(p_from_line, i) != "") { - text.set_line_gutter_metadata(p_to_line, i, text.get_line_gutter_metadata(p_from_line, i)); - } +void TextEdit::insert_line_at(int p_at, const String &p_text) { + ERR_FAIL_INDEX(p_at, text.size()); - if (text.is_line_gutter_clickable(p_from_line, i)) { - text.set_line_gutter_clickable(p_to_line, i, true); + _insert_text(p_at, 0, p_text + "\n"); + if (caret.line >= p_at) { + // offset caret when located after inserted line + ++caret.line; + } + if (has_selection()) { + if (selection.from_line >= p_at) { + // offset selection when located after inserted line + ++selection.from_line; + ++selection.to_line; + } else if (selection.to_line >= p_at) { + // extend selection that includes inserted line + ++selection.to_line; } } - update(); } -void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) { - ERR_FAIL_INDEX(p_gutter, gutters.size()); - ERR_FAIL_NULL(p_object); +void TextEdit::insert_text_at_caret(const String &p_text) { + delete_selection(); - gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id(); - gutters.write[p_gutter].custom_draw_callback = p_callback; + int new_column, new_line; + _insert_text(caret.line, caret.column, p_text, &new_line, &new_column); + _update_scrollbars(); + + set_caret_line(new_line, false); + set_caret_column(new_column); update(); } -// Line gutters. -void TextEdit::set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) { - ERR_FAIL_INDEX(p_line, text.size()); - ERR_FAIL_INDEX(p_gutter, gutters.size()); - text.set_line_gutter_metadata(p_line, p_gutter, p_metadata); -} +void TextEdit::remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { + ERR_FAIL_INDEX(p_from_line, text.size()); + ERR_FAIL_INDEX(p_from_column, text[p_from_line].length() + 1); + ERR_FAIL_INDEX(p_to_line, text.size()); + ERR_FAIL_INDEX(p_to_column, text[p_to_line].length() + 1); + ERR_FAIL_COND(p_to_line < p_from_line); + ERR_FAIL_COND(p_to_line == p_from_line && p_to_column < p_from_column); -Variant TextEdit::get_line_gutter_metadata(int p_line, int p_gutter) const { - ERR_FAIL_INDEX_V(p_line, text.size(), ""); - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), ""); - return text.get_line_gutter_metadata(p_line, p_gutter); + _remove_text(p_from_line, p_from_column, p_to_line, p_to_column); } -void TextEdit::set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { - ERR_FAIL_INDEX(p_line, text.size()); - ERR_FAIL_INDEX(p_gutter, gutters.size()); - text.set_line_gutter_text(p_line, p_gutter, p_text); - update(); -} +int TextEdit::get_last_unhidden_line() const { + // Returns the last line in the text that is not hidden. + if (!_is_hiding_enabled()) { + return text.size() - 1; + } -String TextEdit::get_line_gutter_text(int p_line, int p_gutter) const { - ERR_FAIL_INDEX_V(p_line, text.size(), ""); - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), ""); - return text.get_line_gutter_text(p_line, p_gutter); + int last_line; + for (last_line = text.size() - 1; last_line > 0; last_line--) { + if (!_is_line_hidden(last_line)) { + break; + } + } + return last_line; } -void TextEdit::set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) { - ERR_FAIL_INDEX(p_line, text.size()); - ERR_FAIL_INDEX(p_gutter, gutters.size()); - text.set_line_gutter_icon(p_line, p_gutter, p_icon); - update(); -} +int TextEdit::get_next_visible_line_offset_from(int p_line_from, int p_visible_amount) const { + // Returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines). + ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(p_visible_amount)); -Ref<Texture2D> TextEdit::get_line_gutter_icon(int p_line, int p_gutter) const { - ERR_FAIL_INDEX_V(p_line, text.size(), Ref<Texture2D>()); - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Ref<Texture2D>()); - return text.get_line_gutter_icon(p_line, p_gutter); -} + if (!_is_hiding_enabled()) { + return ABS(p_visible_amount); + } -void TextEdit::set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { - ERR_FAIL_INDEX(p_line, text.size()); - ERR_FAIL_INDEX(p_gutter, gutters.size()); - text.set_line_gutter_item_color(p_line, p_gutter, p_color); - update(); + int num_visible = 0; + int num_total = 0; + if (p_visible_amount >= 0) { + for (int i = p_line_from; i < text.size(); i++) { + num_total++; + if (!_is_line_hidden(i)) { + num_visible++; + } + if (num_visible >= p_visible_amount) { + break; + } + } + } else { + p_visible_amount = ABS(p_visible_amount); + for (int i = p_line_from; i >= 0; i--) { + num_total++; + if (!_is_line_hidden(i)) { + num_visible++; + } + if (num_visible >= p_visible_amount) { + break; + } + } + } + return num_total; } -Color TextEdit::get_line_gutter_item_color(int p_line, int p_gutter) { - ERR_FAIL_INDEX_V(p_line, text.size(), Color()); - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Color()); - return text.get_line_gutter_item_color(p_line, p_gutter); -} +Point2i TextEdit::get_next_visible_line_index_offset_from(int p_line_from, int p_wrap_index_from, int p_visible_amount) const { + // Returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows). + // Wrap index is set to the wrap index of the last line. + int wrap_index = 0; + ERR_FAIL_INDEX_V(p_line_from, text.size(), Point2i(ABS(p_visible_amount), 0)); -void TextEdit::set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) { - ERR_FAIL_INDEX(p_line, text.size()); - ERR_FAIL_INDEX(p_gutter, gutters.size()); - text.set_line_gutter_clickable(p_line, p_gutter, p_clickable); -} + if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { + return Point2i(ABS(p_visible_amount), 0); + } -bool TextEdit::is_line_gutter_clickable(int p_line, int p_gutter) const { - ERR_FAIL_INDEX_V(p_line, text.size(), false); - ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); - return text.is_line_gutter_clickable(p_line, p_gutter); + int num_visible = 0; + int num_total = 0; + if (p_visible_amount == 0) { + num_total = 0; + wrap_index = 0; + } else if (p_visible_amount > 0) { + int i; + num_visible -= p_wrap_index_from; + for (i = p_line_from; i < text.size(); i++) { + num_total++; + if (!_is_line_hidden(i)) { + num_visible++; + num_visible += get_line_wrap_count(i); + } + if (num_visible >= p_visible_amount) { + break; + } + } + wrap_index = get_line_wrap_count(MIN(i, text.size() - 1)) - MAX(0, num_visible - p_visible_amount); + } else { + p_visible_amount = ABS(p_visible_amount); + int i; + num_visible -= get_line_wrap_count(p_line_from) - p_wrap_index_from; + for (i = p_line_from; i >= 0; i--) { + num_total++; + if (!_is_line_hidden(i)) { + num_visible++; + num_visible += get_line_wrap_count(i); + } + if (num_visible >= p_visible_amount) { + break; + } + } + wrap_index = MAX(0, num_visible - p_visible_amount); + } + wrap_index = MAX(wrap_index, 0); + return Point2i(num_total, wrap_index); } -// Line style -void TextEdit::set_line_background_color(int p_line, const Color &p_color) { - ERR_FAIL_INDEX(p_line, text.size()); - text.set_line_background_color(p_line, p_color); - update(); +// Overridable actions +void TextEdit::handle_unicode_input(const uint32_t p_unicode) { + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_handle_unicode_input")) { + si->call("_handle_unicode_input", p_unicode); + return; + } + _handle_unicode_input(p_unicode); } -Color TextEdit::get_line_background_color(int p_line) { - ERR_FAIL_INDEX_V(p_line, text.size(), Color()); - return text.get_line_background_color(p_line); +void TextEdit::backspace() { + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_backspace")) { + si->call("_backspace"); + return; + } + _backspace(); } void TextEdit::cut() { - if (readonly) { + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_cut")) { + si->call("_cut"); return; } - - if (!selection.active) { - String clipboard = text[cursor.line]; - DisplayServer::get_singleton()->clipboard_set(clipboard); - cursor_set_line(cursor.line); - cursor_set_column(0); - - if (cursor.line == 0 && get_line_count() > 1) { - _remove_text(cursor.line, 0, cursor.line + 1, 0); - } else { - _remove_text(cursor.line, 0, cursor.line, text[cursor.line].length()); - backspace(); - cursor_set_line(cursor.line + 1); - } - - update(); - cut_copy_line = clipboard; - - } else { - String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - DisplayServer::get_singleton()->clipboard_set(clipboard); - - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line, false); // Set afterwards else it causes the view to be offset. - cursor_set_column(selection.from_column); - - selection.active = false; - selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; - update(); - cut_copy_line = ""; - } + _cut(); } void TextEdit::copy() { - if (!selection.active) { - if (text[cursor.line].length() != 0) { - String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length()); - DisplayServer::get_singleton()->clipboard_set(clipboard); - cut_copy_line = clipboard; - } - } else { - String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - DisplayServer::get_singleton()->clipboard_set(clipboard); - cut_copy_line = ""; + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_copy")) { + si->call("_copy"); + return; } + _copy(); } void TextEdit::paste() { - if (readonly) { + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_paste")) { + si->call("_paste"); return; } + _paste(); +} - String clipboard = DisplayServer::get_singleton()->clipboard_get(); +// Context menu. +PopupMenu *TextEdit::get_menu() const { + const_cast<TextEdit *>(this)->_generate_context_menu(); + return menu; +} - begin_complex_operation(); - if (selection.active) { - selection.active = false; - selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line, false); - cursor_set_column(selection.from_column); +bool TextEdit::is_menu_visible() const { + return menu && menu->is_visible(); +} - } else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) { - cursor_set_column(0); - String ins = "\n"; - clipboard += ins; +void TextEdit::menu_option(int p_option) { + switch (p_option) { + case MENU_CUT: { + cut(); + } break; + case MENU_COPY: { + copy(); + } break; + case MENU_PASTE: { + paste(); + } break; + case MENU_CLEAR: { + if (editable) { + clear(); + } + } break; + case MENU_SELECT_ALL: { + select_all(); + } break; + case MENU_UNDO: { + undo(); + } break; + case MENU_REDO: { + redo(); + } break; + case MENU_DIR_INHERITED: { + set_text_direction(TEXT_DIRECTION_INHERITED); + } break; + case MENU_DIR_AUTO: { + set_text_direction(TEXT_DIRECTION_AUTO); + } break; + case MENU_DIR_LTR: { + set_text_direction(TEXT_DIRECTION_LTR); + } break; + case MENU_DIR_RTL: { + set_text_direction(TEXT_DIRECTION_RTL); + } break; + case MENU_DISPLAY_UCC: { + set_draw_control_chars(!get_draw_control_chars()); + } break; + case MENU_INSERT_LRM: { + if (editable) { + insert_text_at_caret(String::chr(0x200E)); + } + } break; + case MENU_INSERT_RLM: { + if (editable) { + insert_text_at_caret(String::chr(0x200F)); + } + } break; + case MENU_INSERT_LRE: { + if (editable) { + insert_text_at_caret(String::chr(0x202A)); + } + } break; + case MENU_INSERT_RLE: { + if (editable) { + insert_text_at_caret(String::chr(0x202B)); + } + } break; + case MENU_INSERT_LRO: { + if (editable) { + insert_text_at_caret(String::chr(0x202D)); + } + } break; + case MENU_INSERT_RLO: { + if (editable) { + insert_text_at_caret(String::chr(0x202E)); + } + } break; + case MENU_INSERT_PDF: { + if (editable) { + insert_text_at_caret(String::chr(0x202C)); + } + } break; + case MENU_INSERT_ALM: { + if (editable) { + insert_text_at_caret(String::chr(0x061C)); + } + } break; + case MENU_INSERT_LRI: { + if (editable) { + insert_text_at_caret(String::chr(0x2066)); + } + } break; + case MENU_INSERT_RLI: { + if (editable) { + insert_text_at_caret(String::chr(0x2067)); + } + } break; + case MENU_INSERT_FSI: { + if (editable) { + insert_text_at_caret(String::chr(0x2068)); + } + } break; + case MENU_INSERT_PDI: { + if (editable) { + insert_text_at_caret(String::chr(0x2069)); + } + } break; + case MENU_INSERT_ZWJ: { + if (editable) { + insert_text_at_caret(String::chr(0x200D)); + } + } break; + case MENU_INSERT_ZWNJ: { + if (editable) { + insert_text_at_caret(String::chr(0x200C)); + } + } break; + case MENU_INSERT_WJ: { + if (editable) { + insert_text_at_caret(String::chr(0x2060)); + } + } break; + case MENU_INSERT_SHY: { + if (editable) { + insert_text_at_caret(String::chr(0x00AD)); + } + } } +} - insert_text_at_cursor(clipboard); - end_complex_operation(); - - update(); +/* Versioning */ +void TextEdit::begin_complex_operation() { + _push_current_op(); + next_operation_is_complex = true; } -void TextEdit::select_all() { - if (!selecting_enabled) { - return; - } +void TextEdit::end_complex_operation() { + _push_current_op(); + ERR_FAIL_COND(undo_stack.size() == 0); - if (text.size() == 1 && text[0].length() == 0) { + if (undo_stack.back()->get().chain_forward) { + undo_stack.back()->get().chain_forward = false; return; } - selection.active = true; - selection.from_line = 0; - selection.from_column = 0; - selection.selecting_line = 0; - selection.selecting_column = 0; - selection.to_line = text.size() - 1; - selection.to_column = text[selection.to_line].length(); - selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; - selection.shiftclick_left = true; - cursor_set_line(selection.to_line, false); - cursor_set_column(selection.to_column, false); - update(); + + undo_stack.back()->get().chain_backward = true; } -void TextEdit::select_word_under_caret() { - if (!selecting_enabled) { +void TextEdit::undo() { + if (!editable) { return; } - if (text.size() == 1 && text[0].length() == 0) { - return; + _push_current_op(); + + if (undo_stack_pos == nullptr) { + if (!undo_stack.size()) { + return; // Nothing to undo. + } + + undo_stack_pos = undo_stack.back(); + + } else if (undo_stack_pos == undo_stack.front()) { + return; // At the bottom of the undo stack. + } else { + undo_stack_pos = undo_stack_pos->prev(); } - if (selection.active) { - // Allow toggling selection by pressing the shortcut a second time. - // This is also usable as a general-purpose "deselect" shortcut after - // selecting anything. - deselect(); - return; + deselect(); + + TextOperation op = undo_stack_pos->get(); + _do_text_op(op, true); + if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1)) { + select(op.from_line, op.from_column, op.to_line, op.to_column); } - int begin = 0; - int end = 0; - const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].x <= cursor.column && words[i].y >= cursor.column) { - begin = words[i].x; - end = words[i].y; - break; + current_op.version = op.prev_version; + if (undo_stack_pos->get().chain_backward) { + while (true) { + ERR_BREAK(!undo_stack_pos->prev()); + undo_stack_pos = undo_stack_pos->prev(); + op = undo_stack_pos->get(); + _do_text_op(op, true); + current_op.version = op.prev_version; + if (undo_stack_pos->get().chain_forward) { + break; + } } } - select(cursor.line, begin, cursor.line, end); - // Move the cursor to the end of the word for easier editing. - cursor_set_column(end, false); -} - -void TextEdit::deselect() { - selection.active = false; + _update_scrollbars(); + if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) { + set_caret_line(undo_stack_pos->get().to_line, false); + set_caret_column(undo_stack_pos->get().to_column); + } else { + set_caret_line(undo_stack_pos->get().from_line, false); + set_caret_column(undo_stack_pos->get().from_column); + } update(); } -void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { - if (!selecting_enabled) { +void TextEdit::redo() { + if (!editable) { return; } + _push_current_op(); - if (p_from_line < 0) { - p_from_line = 0; - } else if (p_from_line >= text.size()) { - p_from_line = text.size() - 1; - } - if (p_from_column >= text[p_from_line].length()) { - p_from_column = text[p_from_line].length(); - } - if (p_from_column < 0) { - p_from_column = 0; - } - - if (p_to_line < 0) { - p_to_line = 0; - } else if (p_to_line >= text.size()) { - p_to_line = text.size() - 1; - } - if (p_to_column >= text[p_to_line].length()) { - p_to_column = text[p_to_line].length(); - } - if (p_to_column < 0) { - p_to_column = 0; + if (undo_stack_pos == nullptr) { + return; // Nothing to do. } - selection.from_line = p_from_line; - selection.from_column = p_from_column; - selection.to_line = p_to_line; - selection.to_column = p_to_column; - - selection.active = true; - - if (selection.from_line == selection.to_line) { - if (selection.from_column == selection.to_column) { - selection.active = false; + deselect(); - } else if (selection.from_column > selection.to_column) { - selection.shiftclick_left = false; - SWAP(selection.from_column, selection.to_column); - } else { - selection.shiftclick_left = true; + TextOperation op = undo_stack_pos->get(); + _do_text_op(op, false); + current_op.version = op.version; + if (undo_stack_pos->get().chain_forward) { + while (true) { + ERR_BREAK(!undo_stack_pos->next()); + undo_stack_pos = undo_stack_pos->next(); + op = undo_stack_pos->get(); + _do_text_op(op, false); + current_op.version = op.version; + if (undo_stack_pos->get().chain_backward) { + break; + } } - } else if (selection.from_line > selection.to_line) { - selection.shiftclick_left = false; - SWAP(selection.from_line, selection.to_line); - SWAP(selection.from_column, selection.to_column); - } else { - selection.shiftclick_left = true; } + _update_scrollbars(); + set_caret_line(undo_stack_pos->get().to_line, false); + set_caret_column(undo_stack_pos->get().to_column); + undo_stack_pos = undo_stack_pos->next(); update(); } -void TextEdit::swap_lines(int line1, int line2) { - String tmp = get_line(line1); - String tmp2 = get_line(line2); - set_line(line2, tmp); - set_line(line1, tmp2); -} - -bool TextEdit::is_selection_active() const { - return selection.active; -} - -int TextEdit::get_selection_from_line() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.from_line; -} - -int TextEdit::get_selection_from_column() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.from_column; +void TextEdit::clear_undo_history() { + saved_version = 0; + current_op.type = TextOperation::TYPE_NONE; + undo_stack_pos = nullptr; + undo_stack.clear(); } -int TextEdit::get_selection_to_line() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.to_line; +bool TextEdit::is_insert_text_operation() const { + return (current_op.type == TextOperation::TYPE_INSERT); } -int TextEdit::get_selection_to_column() const { - ERR_FAIL_COND_V(!selection.active, -1); - return selection.to_column; +void TextEdit::tag_saved_version() { + saved_version = get_version(); } -String TextEdit::get_selection_text() const { - if (!selection.active) { - return ""; - } - - return _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); +uint32_t TextEdit::get_version() const { + return current_op.version; } -String TextEdit::get_word_under_cursor() const { - Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(cursor.line)->get_rid()); - for (int i = 0; i < words.size(); i++) { - if (words[i].x <= cursor.column && words[i].y > cursor.column) { - return text[cursor.line].substr(words[i].x, words[i].y - words[i].x); - } - } - return ""; +uint32_t TextEdit::get_saved_version() const { + return saved_version; } +/* Search */ void TextEdit::set_search_text(const String &p_search_text) { search_text = p_search_text; } @@ -4296,72 +3039,12 @@ void TextEdit::set_search_flags(uint32_t p_flags) { search_flags = p_flags; } -void TextEdit::set_current_search_result(int line, int col) { - search_result_line = line; - search_result_col = col; - update(); -} - -void TextEdit::set_highlight_all_occurrences(const bool p_enabled) { - highlight_all_occurrences = p_enabled; - update(); -} - -bool TextEdit::is_highlight_all_occurrences_enabled() const { - return highlight_all_occurrences; -} - -int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) { - int col = -1; - - if (p_key.length() > 0 && p_search.length() > 0) { - if (p_from_column < 0 || p_from_column > p_search.length()) { - p_from_column = 0; - } - - while (col == -1 && p_from_column <= p_search.length()) { - if (p_search_flags & SEARCH_MATCH_CASE) { - col = p_search.find(p_key, p_from_column); - } else { - col = p_search.findn(p_key, p_from_column); - } - - // Whole words only. - if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) { - p_from_column = col; - - if (col > 0 && _is_text_char(p_search[col - 1])) { - col = -1; - } else if ((col + p_key.length()) < p_search.length() && _is_text_char(p_search[col + p_key.length()])) { - col = -1; - } - } - - p_from_column += 1; - } - } - return col; -} - -Dictionary TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const { - int col, line; - if (search(p_key, p_search_flags, p_from_line, p_from_column, line, col)) { - Dictionary result; - result["line"] = line; - result["column"] = col; - return result; - - } else { - return Dictionary(); - } -} - -bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column, int &r_line, int &r_column) const { +Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const { if (p_key.length() == 0) { - return false; + return Point2(-1, -1); } - ERR_FAIL_INDEX_V(p_from_line, text.size(), false); - ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, false); + ERR_FAIL_INDEX_V(p_from_line, text.size(), Point2i(-1, -1)); + ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, Point2i(-1, -1)); // Search through the whole document, but start by current line. @@ -4460,417 +3143,665 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l line++; } } + return (pos == -1) ? Point2i(-1, -1) : Point2i(pos, line); +} - if (pos == -1) { - r_line = -1; - r_column = -1; - return false; +/* Mouse */ +Point2 TextEdit::get_local_mouse_pos() const { + Point2 mp = get_local_mouse_position(); + if (is_layout_rtl()) { + mp.x = get_size().width - mp.x; } + return mp; +} - r_line = line; - r_column = pos; +String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { + Point2i pos = get_line_column_at_pos(p_pos); + int row = pos.y; + int col = pos.x; - return true; -} + String s = text[row]; + if (s.length() == 0) { + return ""; + } + int beg, end; + if (select_word(s, col, beg, end)) { + bool inside_quotes = false; + char32_t selected_quote = '\0'; + int qbegin = 0, qend = 0; + for (int i = 0; i < s.length(); i++) { + if (s[i] == '"' || s[i] == '\'') { + if (i == 0 || s[i - 1] != '\\') { + if (inside_quotes && selected_quote == s[i]) { + qend = i; + inside_quotes = false; + selected_quote = '\0'; + if (col >= qbegin && col <= qend) { + return s.substr(qbegin, qend - qbegin); + } + } else if (!inside_quotes) { + qbegin = i + 1; + inside_quotes = true; + selected_quote = s[i]; + } + } + } + } + + return s.substr(beg, end - beg); + } -void TextEdit::_cursor_changed_emit() { - emit_signal(SNAME("cursor_changed")); - cursor_changed_dirty = false; + return String(); } -void TextEdit::_text_changed_emit() { - emit_signal(SNAME("text_changed")); - text_changed_dirty = false; +Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos) const { + float rows = p_pos.y; + rows -= style_normal->get_margin(SIDE_TOP); + rows /= get_line_height(); + rows += _get_v_scroll_offset(); + int first_vis_line = get_first_visible_line(); + int row = first_vis_line + Math::floor(rows); + int wrap_index = 0; + + if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || _is_hiding_enabled()) { + Point2i f_ofs = get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, rows + (1 * SGN(rows))); + wrap_index = f_ofs.y; + if (rows < 0) { + row = first_vis_line - (f_ofs.x - 1); + } else { + row = first_vis_line + (f_ofs.x - 1); + } + } + + if (row < 0) { + row = 0; + } + + int col = 0; + + if (row >= text.size()) { + row = text.size() - 1; + col = text[row].size(); + } else { + int colx = p_pos.x - (style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding); + colx += caret.x_ofs; + col = _get_char_pos_for_line(colx, row, wrap_index); + if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) { + // Move back one if we are at the end of the row. + Vector<String> rows2 = get_line_wrapped_text(row); + int row_end_col = 0; + for (int i = 0; i < wrap_index + 1; i++) { + row_end_col += rows2[i].length(); + } + if (col >= row_end_col) { + col -= 1; + } + } + + RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); + if (is_layout_rtl()) { + colx = TS->shaped_text_get_size(text_rid).x - colx; + } + col = TS->shaped_text_hit_test_position(text_rid, colx); + } + + return Point2i(col, row); } -void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) { - ERR_FAIL_INDEX(p_line, text.size()); - if (is_hiding_enabled() || !p_hidden) { - text.set_hidden(p_line, p_hidden); +int TextEdit::get_minimap_line_at_pos(const Point2i &p_pos) const { + float rows = p_pos.y; + rows -= style_normal->get_margin(SIDE_TOP); + rows /= (minimap_char_size.y + minimap_line_spacing); + rows += _get_v_scroll_offset(); + + // calculate visible lines + int minimap_visible_lines = get_minimap_visible_lines(); + int visible_rows = get_visible_line_count() + 1; + int first_visible_line = get_first_visible_line() - 1; + int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0); + draw_amount += get_line_wrap_count(first_visible_line + 1); + int minimap_line_height = (minimap_char_size.y + minimap_line_spacing); + + // calculate viewport size and y offset + int viewport_height = (draw_amount - 1) * minimap_line_height; + int control_height = _get_control_height() - viewport_height; + int viewport_offset_y = round(get_scroll_pos_for_line(first_visible_line + 1) * control_height) / ((v_scroll->get_max() <= minimap_visible_lines) ? (minimap_visible_lines - draw_amount) : (v_scroll->get_max() - draw_amount)); + + // calculate the first line. + int num_lines_before = round((viewport_offset_y) / minimap_line_height); + int minimap_line = (v_scroll->get_max() <= minimap_visible_lines) ? -1 : first_visible_line; + if (first_visible_line > 0 && minimap_line >= 0) { + minimap_line -= get_next_visible_line_index_offset_from(first_visible_line, 0, -num_lines_before).x; + minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0); + } else { + minimap_line = 0; } - update(); + + int row = minimap_line + Math::floor(rows); + if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || _is_hiding_enabled()) { + int f_ofs = get_next_visible_line_index_offset_from(minimap_line, caret.wrap_ofs, rows + (1 * SGN(rows))).x - 1; + if (rows < 0) { + row = minimap_line - f_ofs; + } else { + row = minimap_line + f_ofs; + } + } + + if (row < 0) { + row = 0; + } + + if (row >= text.size()) { + row = text.size() - 1; + } + + return row; } -bool TextEdit::is_line_hidden(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), false); - return text.is_hidden(p_line); +bool TextEdit::is_dragging_cursor() const { + return dragging_selection || dragging_minimap; } -void TextEdit::unhide_all_lines() { - for (int i = 0; i < text.size(); i++) { - text.set_hidden(i, false); - } - _update_scrollbars(); +/* Caret */ +void TextEdit::set_caret_type(CaretType p_type) { + caret_type = p_type; update(); } -int TextEdit::num_lines_from(int p_line_from, int visible_amount) const { - // Returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines). - ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount)); +TextEdit::CaretType TextEdit::get_caret_type() const { + return caret_type; +} - if (!is_hiding_enabled()) { - return ABS(visible_amount); - } +void TextEdit::set_caret_blink_enabled(const bool p_enabled) { + caret_blink_enabled = p_enabled; - int num_visible = 0; - int num_total = 0; - if (visible_amount >= 0) { - for (int i = p_line_from; i < text.size(); i++) { - num_total++; - if (!is_line_hidden(i)) { - num_visible++; - } - if (num_visible >= visible_amount) { - break; - } - } - } else { - visible_amount = ABS(visible_amount); - for (int i = p_line_from; i >= 0; i--) { - num_total++; - if (!is_line_hidden(i)) { - num_visible++; - } - if (num_visible >= visible_amount) { - break; - } + if (has_focus()) { + if (p_enabled) { + caret_blink_timer->start(); + } else { + caret_blink_timer->stop(); } } - return num_total; + draw_caret = true; } -int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const { - // Returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows). - // Wrap index is set to the wrap index of the last line. - wrap_index = 0; - ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount)); +bool TextEdit::is_caret_blink_enabled() const { + return caret_blink_enabled; +} + +float TextEdit::get_caret_blink_speed() const { + return caret_blink_timer->get_wait_time(); +} + +void TextEdit::set_caret_blink_speed(const float p_speed) { + ERR_FAIL_COND(p_speed <= 0); + caret_blink_timer->set_wait_time(p_speed); +} + +void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enable) { + move_caret_on_right_click = p_enable; +} + +bool TextEdit::is_move_caret_on_right_click_enabled() const { + return move_caret_on_right_click; +} + +void TextEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) { + caret_mid_grapheme_enabled = p_enabled; +} + +bool TextEdit::is_caret_mid_grapheme_enabled() const { + return caret_mid_grapheme_enabled; +} + +bool TextEdit::is_caret_visible() const { + return caret.visible; +} + +Point2 TextEdit::get_caret_draw_pos() const { + return caret.draw_pos; +} - if (!is_hiding_enabled() && !is_wrap_enabled()) { - return ABS(visible_amount); +void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_hidden, int p_wrap_index) { + if (setting_caret_line) { + return; } - int num_visible = 0; - int num_total = 0; - if (visible_amount == 0) { - num_total = 0; - wrap_index = 0; - } else if (visible_amount > 0) { - int i; - num_visible -= p_wrap_index_from; - for (i = p_line_from; i < text.size(); i++) { - num_total++; - if (!is_line_hidden(i)) { - num_visible++; - num_visible += times_line_wraps(i); - } - if (num_visible >= visible_amount) { - break; + setting_caret_line = true; + if (p_line < 0) { + p_line = 0; + } + + if (p_line >= text.size()) { + p_line = text.size() - 1; + } + + if (!p_can_be_hidden) { + if (_is_line_hidden(CLAMP(p_line, 0, text.size() - 1))) { + int move_down = get_next_visible_line_offset_from(p_line, 1) - 1; + if (p_line + move_down <= text.size() - 1 && !_is_line_hidden(p_line + move_down)) { + p_line += move_down; + } else { + int move_up = get_next_visible_line_offset_from(p_line, -1) - 1; + if (p_line - move_up > 0 && !_is_line_hidden(p_line - move_up)) { + p_line -= move_up; + } else { + WARN_PRINT(("Caret set to hidden line " + itos(p_line) + " and there are no nonhidden lines.")); + } } } - wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - MAX(0, num_visible - visible_amount); - } else { - visible_amount = ABS(visible_amount); - int i; - num_visible -= times_line_wraps(p_line_from) - p_wrap_index_from; - for (i = p_line_from; i >= 0; i--) { - num_total++; - if (!is_line_hidden(i)) { - num_visible++; - num_visible += times_line_wraps(i); - } - if (num_visible >= visible_amount) { - break; - } + } + caret.line = p_line; + + int n_col = _get_char_pos_for_line(caret.last_fit_x, p_line, p_wrap_index); + if (n_col != 0 && get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && p_wrap_index < get_line_wrap_count(p_line)) { + Vector<String> rows = get_line_wrapped_text(p_line); + int row_end_col = 0; + for (int i = 0; i < p_wrap_index + 1; i++) { + row_end_col += rows[i].length(); + } + if (n_col >= row_end_col) { + n_col -= 1; } - wrap_index = MAX(0, num_visible - visible_amount); } - wrap_index = MAX(wrap_index, 0); - return num_total; + caret.column = n_col; + + if (p_adjust_viewport) { + adjust_viewport_to_caret(); + } + + setting_caret_line = false; + + if (!caret_pos_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed"); + } + caret_pos_dirty = true; + } } -int TextEdit::get_last_unhidden_line() const { - // Returns the last line in the text that is not hidden. - if (!is_hiding_enabled()) { - return text.size() - 1; +int TextEdit::get_caret_line() const { + return caret.line; +} + +void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) { + if (p_col < 0) { + p_col = 0; } - int last_line; - for (last_line = text.size() - 1; last_line > 0; last_line--) { - if (!is_line_hidden(last_line)) { - break; + caret.column = p_col; + if (caret.column > get_line(caret.line).length()) { + caret.column = get_line(caret.line).length(); + } + + caret.last_fit_x = _get_column_x_offset_for_line(caret.column, caret.line); + + if (p_adjust_viewport) { + adjust_viewport_to_caret(); + } + + if (!caret_pos_dirty) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed"); } + caret_pos_dirty = true; } - return last_line; } -int TextEdit::get_indent_level(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); +int TextEdit::get_caret_column() const { + return caret.column; +} - int tab_count = 0; - int whitespace_count = 0; - int line_length = text[p_line].size(); - for (int i = 0; i < line_length - 1; i++) { - if (text[p_line][i] == '\t') { - tab_count++; - } else if (text[p_line][i] == ' ') { - whitespace_count++; - } else { - break; +int TextEdit::get_caret_wrap_index() const { + return get_line_wrap_index_at_column(caret.line, caret.column); +} + +String TextEdit::get_word_under_caret() const { + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].x <= caret.column && words[i].y > caret.column) { + return text[caret.line].substr(words[i].x, words[i].y - words[i].x); } } - return tab_count * text.get_tab_size() + whitespace_count; + return ""; } -int TextEdit::get_first_non_whitespace_column(int p_line) const { - ERR_FAIL_INDEX_V(p_line, text.size(), 0); +/* Selection. */ +void TextEdit::set_selecting_enabled(const bool p_enabled) { + selecting_enabled = p_enabled; - int col = 0; - while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) { - col++; + if (!selecting_enabled) { + deselect(); } - return col; } -int TextEdit::get_line_count() const { - return text.size(); +bool TextEdit::is_selecting_enabled() const { + return selecting_enabled; } -int TextEdit::get_line_width(int p_line, int p_wrap_offset) const { - return text.get_line_width(p_line, p_wrap_offset); +void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { + override_selected_font_color = p_override_selected_font_color; } -void TextEdit::_do_text_op(const TextOperation &p_op, bool p_reverse) { - ERR_FAIL_COND(p_op.type == TextOperation::TYPE_NONE); +bool TextEdit::is_overriding_selected_font_color() const { + return override_selected_font_color; +} - bool insert = p_op.type == TextOperation::TYPE_INSERT; - if (p_reverse) { - insert = !insert; +void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column) { + selection.selecting_mode = p_mode; + if (p_line >= 0) { + ERR_FAIL_INDEX(p_line, text.size()); + selection.selecting_line = p_line; } - - if (insert) { - int check_line; - int check_column; - _base_insert_text(p_op.from_line, p_op.from_column, p_op.text, check_line, check_column); - ERR_FAIL_COND(check_line != p_op.to_line); // BUG. - ERR_FAIL_COND(check_column != p_op.to_column); // BUG. - } else { - _base_remove_text(p_op.from_line, p_op.from_column, p_op.to_line, p_op.to_column); + if (p_column >= 0) { + ERR_FAIL_INDEX(p_column, text[selection.selecting_line].length()); + selection.selecting_column = p_column; } } -void TextEdit::_clear_redo() { - if (undo_stack_pos == nullptr) { - return; // Nothing to clear. - } +TextEdit::SelectionMode TextEdit::get_selection_mode() const { + return selection.selecting_mode; +} - _push_current_op(); +void TextEdit::select_all() { + if (!selecting_enabled) { + return; + } - while (undo_stack_pos) { - List<TextOperation>::Element *elem = undo_stack_pos; - undo_stack_pos = undo_stack_pos->next(); - undo_stack.erase(elem); + if (text.size() == 1 && text[0].length() == 0) { + return; } + selection.active = true; + selection.from_line = 0; + selection.from_column = 0; + selection.selecting_line = 0; + selection.selecting_column = 0; + selection.to_line = text.size() - 1; + selection.to_column = text[selection.to_line].length(); + selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; + selection.shiftclick_left = true; + set_caret_line(selection.to_line, false); + set_caret_column(selection.to_column, false); + update(); } -void TextEdit::undo() { - if (readonly) { +void TextEdit::select_word_under_caret() { + if (!selecting_enabled) { return; } - _push_current_op(); + if (text.size() == 1 && text[0].length() == 0) { + return; + } - if (undo_stack_pos == nullptr) { - if (!undo_stack.size()) { - return; // Nothing to undo. + if (selection.active) { + /* Allow toggling selection by pressing the shortcut a second time. */ + /* This is also usable as a general-purpose "deselect" shortcut after */ + /* selecting anything. */ + deselect(); + return; + } + + int begin = 0; + int end = 0; + const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].x <= caret.column && words[i].y >= caret.column) { + begin = words[i].x; + end = words[i].y; + break; } + } - undo_stack_pos = undo_stack.back(); + select(caret.line, begin, caret.line, end); + /* Move the caret to the end of the word for easier editing. */ + set_caret_column(end, false); +} - } else if (undo_stack_pos == undo_stack.front()) { - return; // At the bottom of the undo stack. - } else { - undo_stack_pos = undo_stack_pos->prev(); +void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { + if (!selecting_enabled) { + return; } - deselect(); - - TextOperation op = undo_stack_pos->get(); - _do_text_op(op, true); - if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1)) { - select(op.from_line, op.from_column, op.to_line, op.to_column); + if (p_from_line < 0) { + p_from_line = 0; + } else if (p_from_line >= text.size()) { + p_from_line = text.size() - 1; + } + if (p_from_column >= text[p_from_line].length()) { + p_from_column = text[p_from_line].length(); + } + if (p_from_column < 0) { + p_from_column = 0; } - current_op.version = op.prev_version; - if (undo_stack_pos->get().chain_backward) { - while (true) { - ERR_BREAK(!undo_stack_pos->prev()); - undo_stack_pos = undo_stack_pos->prev(); - op = undo_stack_pos->get(); - _do_text_op(op, true); - current_op.version = op.prev_version; - if (undo_stack_pos->get().chain_forward) { - break; - } - } + if (p_to_line < 0) { + p_to_line = 0; + } else if (p_to_line >= text.size()) { + p_to_line = text.size() - 1; + } + if (p_to_column >= text[p_to_line].length()) { + p_to_column = text[p_to_line].length(); + } + if (p_to_column < 0) { + p_to_column = 0; } - _update_scrollbars(); - if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) { - cursor_set_line(undo_stack_pos->get().to_line, false); - cursor_set_column(undo_stack_pos->get().to_column); + selection.from_line = p_from_line; + selection.from_column = p_from_column; + selection.to_line = p_to_line; + selection.to_column = p_to_column; + + selection.active = true; + + if (selection.from_line == selection.to_line) { + if (selection.from_column == selection.to_column) { + selection.active = false; + + } else if (selection.from_column > selection.to_column) { + selection.shiftclick_left = false; + SWAP(selection.from_column, selection.to_column); + } else { + selection.shiftclick_left = true; + } + } else if (selection.from_line > selection.to_line) { + selection.shiftclick_left = false; + SWAP(selection.from_line, selection.to_line); + SWAP(selection.from_column, selection.to_column); } else { - cursor_set_line(undo_stack_pos->get().from_line, false); - cursor_set_column(undo_stack_pos->get().from_column); + selection.shiftclick_left = true; } + update(); } -void TextEdit::redo() { - if (readonly) { - return; - } - _push_current_op(); +bool TextEdit::has_selection() const { + return selection.active; +} - if (undo_stack_pos == nullptr) { - return; // Nothing to do. +String TextEdit::get_selected_text() const { + if (!selection.active) { + return ""; } - deselect(); + return _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); +} - TextOperation op = undo_stack_pos->get(); - _do_text_op(op, false); - current_op.version = op.version; - if (undo_stack_pos->get().chain_forward) { - while (true) { - ERR_BREAK(!undo_stack_pos->next()); - undo_stack_pos = undo_stack_pos->next(); - op = undo_stack_pos->get(); - _do_text_op(op, false); - current_op.version = op.version; - if (undo_stack_pos->get().chain_backward) { - break; - } - } - } +int TextEdit::get_selection_line() const { + return selection.selecting_line; +} - _update_scrollbars(); - cursor_set_line(undo_stack_pos->get().to_line, false); - cursor_set_column(undo_stack_pos->get().to_column); - undo_stack_pos = undo_stack_pos->next(); - update(); +int TextEdit::get_selection_column() const { + return selection.selecting_column; } -void TextEdit::clear_undo_history() { - saved_version = 0; - current_op.type = TextOperation::TYPE_NONE; - undo_stack_pos = nullptr; - undo_stack.clear(); +int TextEdit::get_selection_from_line() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.from_line; } -void TextEdit::begin_complex_operation() { - _push_current_op(); - next_operation_is_complex = true; +int TextEdit::get_selection_from_column() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.from_column; } -void TextEdit::end_complex_operation() { - _push_current_op(); - ERR_FAIL_COND(undo_stack.size() == 0); +int TextEdit::get_selection_to_line() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.to_line; +} - if (undo_stack.back()->get().chain_forward) { - undo_stack.back()->get().chain_forward = false; +int TextEdit::get_selection_to_column() const { + ERR_FAIL_COND_V(!selection.active, -1); + return selection.to_column; +} + +void TextEdit::deselect() { + selection.active = false; + update(); +} + +void TextEdit::delete_selection() { + if (!has_selection()) { return; } - undo_stack.back()->get().chain_backward = true; + selection.active = false; + selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; + _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); + set_caret_line(selection.from_line, false, false); + set_caret_column(selection.from_column); + update(); } -void TextEdit::_push_current_op() { - if (current_op.type == TextOperation::TYPE_NONE) { - return; // Nothing to do. +/* line wrapping. */ +void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) { + if (line_wrapping_mode != p_wrapping_mode) { + line_wrapping_mode = p_wrapping_mode; + _update_wrap_at_column(true); } +} - if (next_operation_is_complex) { - current_op.chain_forward = true; - next_operation_is_complex = false; +TextEdit::LineWrappingMode TextEdit::get_line_wrapping_mode() const { + return line_wrapping_mode; +} + +bool TextEdit::is_line_wrapped(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { + return false; } + return text.get_line_wrap_amount(p_line) > 0; +} - undo_stack.push_back(current_op); - current_op.type = TextOperation::TYPE_NONE; - current_op.text = ""; - current_op.chain_forward = false; +int TextEdit::get_line_wrap_count(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); - if (undo_stack.size() > undo_stack_max_size) { - undo_stack.pop_front(); + if (!is_line_wrapped(p_line)) { + return 0; } + + return text.get_line_wrap_amount(p_line); } -void TextEdit::set_tab_size(const int p_size) { - ERR_FAIL_COND_MSG(p_size <= 0, "Tab size must be greater than 0."); - if (p_size == text.get_tab_size()) { - return; +int TextEdit::get_line_wrap_index_at_column(int p_line, int p_column) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + ERR_FAIL_COND_V(p_column < 0, 0); + ERR_FAIL_COND_V(p_column > text[p_line].length(), 0); + + if (!is_line_wrapped(p_line)) { + return 0; } - text.set_tab_size(p_size); - text.invalidate_all_lines(); - update(); -} -int TextEdit::get_tab_size() const { - return text.get_tab_size(); + /* Loop through wraps in the line text until we get to the column. */ + int wrap_index = 0; + int col = 0; + Vector<String> lines = get_line_wrapped_text(p_line); + for (int i = 0; i < lines.size(); i++) { + wrap_index = i; + String s = lines[wrap_index]; + col += s.length(); + if (col > p_column) { + break; + } + } + return wrap_index; } -void TextEdit::set_draw_tabs(bool p_draw) { - draw_tabs = p_draw; - update(); -} +Vector<String> TextEdit::get_line_wrapped_text(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>()); -bool TextEdit::is_drawing_tabs() const { - return draw_tabs; + Vector<String> lines; + if (!is_line_wrapped(p_line)) { + lines.push_back(text[p_line]); + return lines; + } + + const String &line_text = text[p_line]; + Vector<Vector2i> line_ranges = text.get_line_wrap_ranges(p_line); + for (int i = 0; i < line_ranges.size(); i++) { + lines.push_back(line_text.substr(line_ranges[i].x, line_ranges[i].y - line_ranges[i].x)); + } + + return lines; } -void TextEdit::set_draw_spaces(bool p_draw) { - draw_spaces = p_draw; - update(); +/* Viewport */ +// Scrolling. +void TextEdit::set_smooth_scroll_enabled(const bool p_enable) { + v_scroll->set_smooth_scroll_enabled(p_enable); + smooth_scroll_enabled = p_enable; } -bool TextEdit::is_drawing_spaces() const { - return draw_spaces; +bool TextEdit::is_smooth_scroll_enabled() const { + return smooth_scroll_enabled; } -void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { - override_selected_font_color = p_override_selected_font_color; +void TextEdit::set_scroll_past_end_of_file_enabled(const bool p_enabled) { + scroll_past_end_of_file_enabled = p_enabled; + update(); } -bool TextEdit::is_overriding_selected_font_color() const { - return override_selected_font_color; +bool TextEdit::is_scroll_past_end_of_file_enabled() const { + return scroll_past_end_of_file_enabled; } -void TextEdit::set_insert_mode(bool p_enabled) { - insert_mode = p_enabled; - update(); +void TextEdit::set_v_scroll(double p_scroll) { + v_scroll->set_value(p_scroll); + int max_v_scroll = v_scroll->get_max() - v_scroll->get_page(); + if (p_scroll >= max_v_scroll - 1.0) { + _scroll_moved(v_scroll->get_value()); + } } -bool TextEdit::is_insert_mode() const { - return insert_mode; +double TextEdit::get_v_scroll() const { + return v_scroll->get_value(); } -bool TextEdit::is_insert_text_operation() { - return (current_op.type == TextOperation::TYPE_INSERT); +void TextEdit::set_h_scroll(int p_scroll) { + if (p_scroll < 0) { + p_scroll = 0; + } + h_scroll->set_value(p_scroll); } -uint32_t TextEdit::get_version() const { - return current_op.version; +int TextEdit::get_h_scroll() const { + return h_scroll->get_value(); } -uint32_t TextEdit::get_saved_version() const { - return saved_version; +void TextEdit::set_v_scroll_speed(float p_speed) { + v_scroll_speed = p_speed; } -void TextEdit::tag_saved_version() { - saved_version = get_version(); +float TextEdit::get_v_scroll_speed() const { + return v_scroll_speed; } double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const { - if (!is_wrap_enabled() && !is_hiding_enabled()) { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + ERR_FAIL_COND_V(p_wrap_index < 0, 0); + ERR_FAIL_COND_V(p_wrap_index > get_line_wrap_count(p_line), 0); + + if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE && !_is_hiding_enabled()) { return p_line; } @@ -4880,206 +3811,207 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const { for (int i = 0; i < to; i++) { if (!text.is_hidden(i)) { new_line_scroll_pos++; - new_line_scroll_pos += times_line_wraps(i); + new_line_scroll_pos += get_line_wrap_count(i); } } new_line_scroll_pos += p_wrap_index; return new_line_scroll_pos; } +// Visible lines. void TextEdit::set_line_as_first_visible(int p_line, int p_wrap_index) { + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_COND(p_wrap_index < 0); + ERR_FAIL_COND(p_wrap_index > get_line_wrap_count(p_line)); set_v_scroll(get_scroll_pos_for_line(p_line, p_wrap_index)); } +int TextEdit::get_first_visible_line() const { + return CLAMP(caret.line_ofs, 0, text.size() - 1); +} + void TextEdit::set_line_as_center_visible(int p_line, int p_wrap_index) { - int visible_rows = get_visible_rows(); - int wi; - int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -visible_rows / 2, wi) + 1; + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_COND(p_wrap_index < 0); + ERR_FAIL_COND(p_wrap_index > get_line_wrap_count(p_line)); + + int visible_rows = get_visible_line_count(); + Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, -visible_rows / 2); + int first_line = p_line - next_line.x + 1; - set_v_scroll(get_scroll_pos_for_line(first_line, wi)); + set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y)); } void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) { - int wi; - int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -get_visible_rows() - 1, wi) + 1; + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_COND(p_wrap_index < 0); + ERR_FAIL_COND(p_wrap_index > get_line_wrap_count(p_line)); - set_v_scroll(get_scroll_pos_for_line(first_line, wi) + get_visible_rows_offset()); -} + Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, -get_visible_line_count() - 1); + int first_line = p_line - next_line.x + 1; -int TextEdit::get_first_visible_line() const { - return CLAMP(cursor.line_ofs, 0, text.size() - 1); + set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y) + _get_visible_lines_offset()); } int TextEdit::get_last_full_visible_line() const { int first_vis_line = get_first_visible_line(); int last_vis_line = 0; - int wi; - last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows(), wi) - 1; + last_vis_line = first_vis_line + get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, get_visible_line_count()).x - 1; last_vis_line = CLAMP(last_vis_line, 0, text.size() - 1); return last_vis_line; } int TextEdit::get_last_full_visible_line_wrap_index() const { int first_vis_line = get_first_visible_line(); - int wi; - num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows(), wi); - return wi; -} - -double TextEdit::get_visible_rows_offset() const { - double total = _get_control_height(); - total /= (double)get_row_height(); - total = total - floor(total); - total = -CLAMP(total, 0.001, 1) + 1; - return total; + return get_next_visible_line_index_offset_from(first_vis_line, caret.wrap_ofs, get_visible_line_count()).y; } -double TextEdit::get_v_scroll_offset() const { - double val = get_v_scroll() - floor(get_v_scroll()); - return CLAMP(val, 0, 1); +int TextEdit::get_visible_line_count() const { + return _get_control_height() / get_line_height(); } -double TextEdit::get_v_scroll() const { - return v_scroll->get_value(); -} +int TextEdit::get_total_visible_line_count() const { + /* Returns the total number of (lines + wraped - hidden). */ + if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { + return text.size(); + } -void TextEdit::set_v_scroll(double p_scroll) { - v_scroll->set_value(p_scroll); - int max_v_scroll = v_scroll->get_max() - v_scroll->get_page(); - if (p_scroll >= max_v_scroll - 1.0) { - _scroll_moved(v_scroll->get_value()); + int total_rows = 0; + for (int i = 0; i < text.size(); i++) { + if (!text.is_hidden(i)) { + total_rows++; + total_rows += get_line_wrap_count(i); + } } + return total_rows; } -int TextEdit::get_h_scroll() const { - return h_scroll->get_value(); -} +// Auto adjust +void TextEdit::adjust_viewport_to_caret() { + // Make sure Caret is visible on the screen. + scrolling = false; + minimap_clicked = false; -void TextEdit::set_h_scroll(int p_scroll) { - if (p_scroll < 0) { - p_scroll = 0; - } - h_scroll->set_value(p_scroll); -} + int cur_line = caret.line; + int cur_wrap = get_caret_wrap_index(); -void TextEdit::set_smooth_scroll_enabled(bool p_enable) { - v_scroll->set_smooth_scroll_enabled(p_enable); - smooth_scroll_enabled = p_enable; -} + int first_vis_line = get_first_visible_line(); + int first_vis_wrap = caret.wrap_ofs; + int last_vis_line = get_last_full_visible_line(); + int last_vis_wrap = get_last_full_visible_line_wrap_index(); -bool TextEdit::is_smooth_scroll_enabled() const { - return smooth_scroll_enabled; -} + if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) { + // Caret is above screen. + set_line_as_first_visible(cur_line, cur_wrap); + } else if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) { + // Caret is below screen. + set_line_as_last_visible(cur_line, cur_wrap); + } -void TextEdit::set_v_scroll_speed(float p_speed) { - v_scroll_speed = p_speed; -} + int visible_width = get_size().width - style_normal->get_minimum_size().width - gutters_width - gutter_padding; + if (draw_minimap) { + visible_width -= minimap_width; + } + if (v_scroll->is_visible_in_tree()) { + visible_width -= v_scroll->get_combined_minimum_size().width; + } + visible_width -= 20; // Give it a little more space. -float TextEdit::get_v_scroll_speed() const { - return v_scroll_speed; -} + if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { + // Adjust x offset. + Vector2i caret_pos; -String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { - int row, col; - _get_mouse_pos(p_pos, row, col); + // Get position of the start of caret. + if (ime_text.length() != 0 && ime_selection.x != 0) { + caret_pos.x = _get_column_x_offset_for_line(caret.column + ime_selection.x, caret.line); + } else { + caret_pos.x = _get_column_x_offset_for_line(caret.column, caret.line); + } - String s = text[row]; - if (s.length() == 0) { - return ""; - } - int beg, end; - if (select_word(s, col, beg, end)) { - bool inside_quotes = false; - char32_t selected_quote = '\0'; - int qbegin = 0, qend = 0; - for (int i = 0; i < s.length(); i++) { - if (s[i] == '"' || s[i] == '\'') { - if (i == 0 || s[i - 1] != '\\') { - if (inside_quotes && selected_quote == s[i]) { - qend = i; - inside_quotes = false; - selected_quote = '\0'; - if (col >= qbegin && col <= qend) { - return s.substr(qbegin, qend - qbegin); - } - } else if (!inside_quotes) { - qbegin = i + 1; - inside_quotes = true; - selected_quote = s[i]; - } - } + // Get position of the end of caret. + if (ime_text.length() != 0) { + if (ime_selection.y != 0) { + caret_pos.y = _get_column_x_offset_for_line(caret.column + ime_selection.x + ime_selection.y, caret.line); + } else { + caret_pos.y = _get_column_x_offset_for_line(caret.column + ime_text.size(), caret.line); } + } else { + caret_pos.y = caret_pos.x; } - return s.substr(beg, end - beg); + if (MAX(caret_pos.x, caret_pos.y) > (caret.x_ofs + visible_width)) { + caret.x_ofs = MAX(caret_pos.x, caret_pos.y) - visible_width + 1; + } + + if (MIN(caret_pos.x, caret_pos.y) < caret.x_ofs) { + caret.x_ofs = MIN(caret_pos.x, caret_pos.y); + } + } else { + caret.x_ofs = 0; } + h_scroll->set_value(caret.x_ofs); - return String(); + update(); } -String TextEdit::get_tooltip(const Point2 &p_pos) const { - if (!tooltip_obj) { - return Control::get_tooltip(p_pos); - } - int row, col; - _get_mouse_pos(p_pos, row, col); +void TextEdit::center_viewport_to_caret() { + // Move viewport so the caret is in the center of the screen. + scrolling = false; + minimap_clicked = false; - String s = text[row]; - if (s.length() == 0) { - return Control::get_tooltip(p_pos); + set_line_as_center_visible(caret.line, get_caret_wrap_index()); + int visible_width = get_size().width - style_normal->get_minimum_size().width - gutters_width - gutter_padding; + if (draw_minimap) { + visible_width -= minimap_width; } - int beg, end; - if (select_word(s, col, beg, end)) { - String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud); - - return tt; + if (v_scroll->is_visible_in_tree()) { + visible_width -= v_scroll->get_combined_minimum_size().width; } + visible_width -= 20; // Give it a little more space. - return Control::get_tooltip(p_pos); -} + if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE) { + // Center x offset. -void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) { - tooltip_obj = p_obj; - tooltip_func = p_function; - tooltip_ud = p_udata; -} + Vector2i caret_pos; -void TextEdit::set_line(int line, String new_text) { - if (line < 0 || line >= text.size()) { - return; - } - _remove_text(line, 0, line, text[line].length()); - _insert_text(line, 0, new_text); - if (cursor.line == line) { - cursor.column = MIN(cursor.column, new_text.length()); - } - if (is_selection_active() && line == selection.to_line && selection.to_column > text[line].length()) { - selection.to_column = text[line].length(); - } -} + // Get position of the start of caret. + if (ime_text.length() != 0 && ime_selection.x != 0) { + caret_pos.x = _get_column_x_offset_for_line(caret.column + ime_selection.x, caret.line); + } else { + caret_pos.x = _get_column_x_offset_for_line(caret.column, caret.line); + } -void TextEdit::insert_at(const String &p_text, int at) { - _insert_text(at, 0, p_text + "\n"); - if (cursor.line >= at) { - // offset cursor when located after inserted line - ++cursor.line; - } - if (is_selection_active()) { - if (selection.from_line >= at) { - // offset selection when located after inserted line - ++selection.from_line; - ++selection.to_line; - } else if (selection.to_line >= at) { - // extend selection that includes inserted line - ++selection.to_line; + // Get position of the end of caret. + if (ime_text.length() != 0) { + if (ime_selection.y != 0) { + caret_pos.y = _get_column_x_offset_for_line(caret.column + ime_selection.x + ime_selection.y, caret.line); + } else { + caret_pos.y = _get_column_x_offset_for_line(caret.column + ime_text.size(), caret.line); + } + } else { + caret_pos.y = caret_pos.x; + } + + if (MAX(caret_pos.x, caret_pos.y) > (caret.x_ofs + visible_width)) { + caret.x_ofs = MAX(caret_pos.x, caret_pos.y) - visible_width + 1; } + + if (MIN(caret_pos.x, caret_pos.y) < caret.x_ofs) { + caret.x_ofs = MIN(caret_pos.x, caret_pos.y); + } + } else { + caret.x_ofs = 0; } + h_scroll->set_value(caret.x_ofs); + + update(); } +/* Minimap */ void TextEdit::set_draw_minimap(bool p_draw) { if (draw_minimap != p_draw) { draw_minimap = p_draw; - _update_wrap_at(); + _update_wrap_at_column(); } update(); } @@ -5091,7 +4023,7 @@ bool TextEdit::is_drawing_minimap() const { void TextEdit::set_minimap_width(int p_minimap_width) { if (minimap_width != p_minimap_width) { minimap_width = p_minimap_width; - _update_wrap_at(); + _update_wrap_at_column(); } update(); } @@ -5100,391 +4032,572 @@ int TextEdit::get_minimap_width() const { return minimap_width; } -void TextEdit::set_hiding_enabled(bool p_enabled) { - if (!p_enabled) { - unhide_all_lines(); +int TextEdit::get_minimap_visible_lines() const { + return _get_control_height() / (minimap_char_size.y + minimap_line_spacing); +} + +/* Gutters. */ +void TextEdit::add_gutter(int p_at) { + if (p_at < 0 || p_at > gutters.size()) { + gutters.push_back(GutterInfo()); + } else { + gutters.insert(p_at, GutterInfo()); } - hiding_enabled = p_enabled; + + for (int i = 0; i < text.size() + 1; i++) { + text.add_gutter(p_at); + } + emit_signal(SNAME("gutter_added")); update(); } -bool TextEdit::is_hiding_enabled() const { - return hiding_enabled; +void TextEdit::remove_gutter(int p_gutter) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + + gutters.remove(p_gutter); + + for (int i = 0; i < text.size() + 1; i++) { + text.remove_gutter(p_gutter); + } + emit_signal(SNAME("gutter_removed")); + update(); } -void TextEdit::set_highlight_current_line(bool p_enabled) { - highlight_current_line = p_enabled; +int TextEdit::get_gutter_count() const { + return gutters.size(); +} + +void TextEdit::set_gutter_name(int p_gutter, const String &p_name) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + gutters.write[p_gutter].name = p_name; +} + +String TextEdit::get_gutter_name(int p_gutter) const { + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), ""); + return gutters[p_gutter].name; +} + +void TextEdit::set_gutter_type(int p_gutter, GutterType p_type) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + gutters.write[p_gutter].type = p_type; update(); } -bool TextEdit::is_highlight_current_line_enabled() const { - return highlight_current_line; +TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const { + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), GUTTER_TYPE_STRING); + return gutters[p_gutter].type; } -bool TextEdit::is_text_field() const { - return true; +void TextEdit::set_gutter_width(int p_gutter, int p_width) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + gutters.write[p_gutter].width = p_width; + _update_gutter_width(); } -void TextEdit::menu_option(int p_option) { - switch (p_option) { - case MENU_CUT: { - if (!readonly) { - cut(); - } - } break; - case MENU_COPY: { - copy(); - } break; - case MENU_PASTE: { - if (!readonly) { - paste(); - } - } break; - case MENU_CLEAR: { - if (!readonly) { - clear(); - } - } break; - case MENU_SELECT_ALL: { - select_all(); - } break; - case MENU_UNDO: { - undo(); - } break; - case MENU_REDO: { - redo(); - } break; - case MENU_DIR_INHERITED: { - set_text_direction(TEXT_DIRECTION_INHERITED); - } break; - case MENU_DIR_AUTO: { - set_text_direction(TEXT_DIRECTION_AUTO); - } break; - case MENU_DIR_LTR: { - set_text_direction(TEXT_DIRECTION_LTR); - } break; - case MENU_DIR_RTL: { - set_text_direction(TEXT_DIRECTION_RTL); - } break; - case MENU_DISPLAY_UCC: { - set_draw_control_chars(!get_draw_control_chars()); - } break; - case MENU_INSERT_LRM: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x200E)); - } - } break; - case MENU_INSERT_RLM: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x200F)); - } - } break; - case MENU_INSERT_LRE: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x202A)); - } - } break; - case MENU_INSERT_RLE: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x202B)); - } - } break; - case MENU_INSERT_LRO: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x202D)); - } - } break; - case MENU_INSERT_RLO: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x202E)); - } - } break; - case MENU_INSERT_PDF: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x202C)); - } - } break; - case MENU_INSERT_ALM: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x061C)); - } - } break; - case MENU_INSERT_LRI: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x2066)); - } - } break; - case MENU_INSERT_RLI: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x2067)); - } - } break; - case MENU_INSERT_FSI: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x2068)); - } - } break; - case MENU_INSERT_PDI: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x2069)); - } - } break; - case MENU_INSERT_ZWJ: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x200D)); - } - } break; - case MENU_INSERT_ZWNJ: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x200C)); - } - } break; - case MENU_INSERT_WJ: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x2060)); - } - } break; - case MENU_INSERT_SHY: { - if (!readonly) { - insert_text_at_cursor(String::chr(0x00AD)); - } +int TextEdit::get_gutter_width(int p_gutter) const { + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), -1); + return gutters[p_gutter].width; +} + +int TextEdit::get_total_gutter_width() const { + return gutters_width + gutter_padding; +} + +void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + gutters.write[p_gutter].draw = p_draw; + _update_gutter_width(); +} + +bool TextEdit::is_gutter_drawn(int p_gutter) const { + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); + return gutters[p_gutter].draw; +} + +void TextEdit::set_gutter_clickable(int p_gutter, bool p_clickable) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + gutters.write[p_gutter].clickable = p_clickable; + update(); +} + +bool TextEdit::is_gutter_clickable(int p_gutter) const { + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); + return gutters[p_gutter].clickable; +} + +void TextEdit::set_gutter_overwritable(int p_gutter, bool p_overwritable) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + gutters.write[p_gutter].overwritable = p_overwritable; +} + +bool TextEdit::is_gutter_overwritable(int p_gutter) const { + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); + return gutters[p_gutter].overwritable; +} + +void TextEdit::merge_gutters(int p_from_line, int p_to_line) { + ERR_FAIL_INDEX(p_from_line, text.size()); + ERR_FAIL_INDEX(p_to_line, text.size()); + if (p_from_line == p_to_line) { + return; + } + + for (int i = 0; i < gutters.size(); i++) { + if (!gutters[i].overwritable) { + continue; + } + + if (text.get_line_gutter_text(p_from_line, i) != "") { + text.set_line_gutter_text(p_to_line, i, text.get_line_gutter_text(p_from_line, i)); + text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i)); + } + + if (text.get_line_gutter_icon(p_from_line, i).is_valid()) { + text.set_line_gutter_icon(p_to_line, i, text.get_line_gutter_icon(p_from_line, i)); + text.set_line_gutter_item_color(p_to_line, i, text.get_line_gutter_item_color(p_from_line, i)); + } + + if (text.get_line_gutter_metadata(p_from_line, i) != "") { + text.set_line_gutter_metadata(p_to_line, i, text.get_line_gutter_metadata(p_from_line, i)); + } + + if (text.is_line_gutter_clickable(p_from_line, i)) { + text.set_line_gutter_clickable(p_to_line, i, true); } } + update(); } -void TextEdit::_set_symbol_lookup_word(const String &p_symbol) { - lookup_symbol_word = p_symbol; +void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) { + ERR_FAIL_INDEX(p_gutter, gutters.size()); + ERR_FAIL_NULL(p_object); + + gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id(); + gutters.write[p_gutter].custom_draw_callback = p_callback; update(); } -void TextEdit::set_context_menu_enabled(bool p_enable) { - context_menu_enabled = p_enable; +// Line gutters. +void TextEdit::set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) { + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_INDEX(p_gutter, gutters.size()); + text.set_line_gutter_metadata(p_line, p_gutter, p_metadata); } -bool TextEdit::is_context_menu_enabled() { - return context_menu_enabled; +Variant TextEdit::get_line_gutter_metadata(int p_line, int p_gutter) const { + ERR_FAIL_INDEX_V(p_line, text.size(), ""); + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), ""); + return text.get_line_gutter_metadata(p_line, p_gutter); } -void TextEdit::set_shortcut_keys_enabled(bool p_enabled) { - shortcut_keys_enabled = p_enabled; +void TextEdit::set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_INDEX(p_gutter, gutters.size()); + text.set_line_gutter_text(p_line, p_gutter, p_text); + update(); } -void TextEdit::set_virtual_keyboard_enabled(bool p_enable) { - virtual_keyboard_enabled = p_enable; +String TextEdit::get_line_gutter_text(int p_line, int p_gutter) const { + ERR_FAIL_INDEX_V(p_line, text.size(), ""); + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), ""); + return text.get_line_gutter_text(p_line, p_gutter); } -void TextEdit::set_selecting_enabled(bool p_enabled) { - selecting_enabled = p_enabled; +void TextEdit::set_line_gutter_icon(int p_line, int p_gutter, const Ref<Texture2D> &p_icon) { + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_INDEX(p_gutter, gutters.size()); + text.set_line_gutter_icon(p_line, p_gutter, p_icon); + update(); +} - if (!selecting_enabled) { - deselect(); - } +Ref<Texture2D> TextEdit::get_line_gutter_icon(int p_line, int p_gutter) const { + ERR_FAIL_INDEX_V(p_line, text.size(), Ref<Texture2D>()); + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Ref<Texture2D>()); + return text.get_line_gutter_icon(p_line, p_gutter); } -bool TextEdit::is_selecting_enabled() const { - return selecting_enabled; +void TextEdit::set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_INDEX(p_gutter, gutters.size()); + text.set_line_gutter_item_color(p_line, p_gutter, p_color); + update(); } -bool TextEdit::is_shortcut_keys_enabled() const { - return shortcut_keys_enabled; +Color TextEdit::get_line_gutter_item_color(int p_line, int p_gutter) const { + ERR_FAIL_INDEX_V(p_line, text.size(), Color()); + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Color()); + return text.get_line_gutter_item_color(p_line, p_gutter); } -bool TextEdit::is_virtual_keyboard_enabled() const { - return virtual_keyboard_enabled; +void TextEdit::set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) { + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_INDEX(p_gutter, gutters.size()); + text.set_line_gutter_clickable(p_line, p_gutter, p_clickable); } -bool TextEdit::is_menu_visible() const { - return menu && menu->is_visible(); +bool TextEdit::is_line_gutter_clickable(int p_line, int p_gutter) const { + ERR_FAIL_INDEX_V(p_line, text.size(), false); + ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false); + return text.is_line_gutter_clickable(p_line, p_gutter); } -PopupMenu *TextEdit::get_menu() const { - const_cast<TextEdit *>(this)->_ensure_menu(); - return menu; +// Line style +void TextEdit::set_line_background_color(int p_line, const Color &p_color) { + ERR_FAIL_INDEX(p_line, text.size()); + text.set_line_background_color(p_line, p_color); + update(); } -bool TextEdit::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - double value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - text.set_font_features(opentype_features); - text.invalidate_all(); - update(); - } - } else { - if ((double)opentype_features[tag] != value) { - opentype_features[tag] = value; - text.set_font_features(opentype_features); - text.invalidate_all(); - ; - update(); - } - } - notify_property_list_changed(); - return true; +Color TextEdit::get_line_background_color(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), Color()); + return text.get_line_background_color(p_line); +} + +/* Syntax Highlighting. */ +void TextEdit::set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter) { + syntax_highlighter = p_syntax_highlighter; + if (syntax_highlighter.is_valid()) { + syntax_highlighter->set_text_edit(this); } + update(); +} - return false; +Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() const { + return syntax_highlighter; } -bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; +/* Visual. */ +void TextEdit::set_highlight_current_line(bool p_enabled) { + highlight_current_line = p_enabled; + update(); } -void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); +bool TextEdit::is_highlight_current_line_enabled() const { + return highlight_current_line; +} + +void TextEdit::set_highlight_all_occurrences(const bool p_enabled) { + highlight_all_occurrences = p_enabled; + update(); +} + +bool TextEdit::is_highlight_all_occurrences_enabled() const { + return highlight_all_occurrences; +} + +void TextEdit::set_draw_control_chars(bool p_draw_control_chars) { + if (draw_control_chars != p_draw_control_chars) { + draw_control_chars = p_draw_control_chars; + menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); + text.set_draw_control_chars(draw_control_chars); + text.invalidate_all(); + update(); } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); +} + +bool TextEdit::get_draw_control_chars() const { + return draw_control_chars; +} + +void TextEdit::set_draw_tabs(bool p_draw) { + draw_tabs = p_draw; + update(); +} + +bool TextEdit::is_drawing_tabs() const { + return draw_tabs; +} + +void TextEdit::set_draw_spaces(bool p_draw) { + draw_spaces = p_draw; + update(); +} + +bool TextEdit::is_drawing_spaces() const { + return draw_spaces; } void TextEdit::_bind_methods() { + /*Internal. */ ClassDB::bind_method(D_METHOD("_gui_input"), &TextEdit::_gui_input); - ClassDB::bind_method(D_METHOD("_cursor_changed_emit"), &TextEdit::_cursor_changed_emit); ClassDB::bind_method(D_METHOD("_text_changed_emit"), &TextEdit::_text_changed_emit); - ClassDB::bind_method(D_METHOD("_update_wrap_at", "force"), &TextEdit::_update_wrap_at, DEFVAL(false)); - BIND_ENUM_CONSTANT(SEARCH_MATCH_CASE); - BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS); - BIND_ENUM_CONSTANT(SEARCH_BACKWARDS); - - BIND_ENUM_CONSTANT(SELECTION_MODE_NONE); - BIND_ENUM_CONSTANT(SELECTION_MODE_SHIFT); - BIND_ENUM_CONSTANT(SELECTION_MODE_POINTER); - BIND_ENUM_CONSTANT(SELECTION_MODE_WORD); - BIND_ENUM_CONSTANT(SELECTION_MODE_LINE); + /* Text */ + // Text properties + ClassDB::bind_method(D_METHOD("has_ime_text"), &TextEdit::has_ime_text); - /* - ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char); - ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line); -*/ + ClassDB::bind_method(D_METHOD("set_editable", "enable"), &TextEdit::set_editable); + ClassDB::bind_method(D_METHOD("is_editable"), &TextEdit::is_editable); - ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &TextEdit::get_draw_control_chars); - ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &TextEdit::set_draw_control_chars); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &TextEdit::get_text_direction); + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextEdit::set_opentype_feature); ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextEdit::get_opentype_feature); ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextEdit::clear_opentype_features); + ClassDB::bind_method(D_METHOD("set_language", "language"), &TextEdit::set_language); ClassDB::bind_method(D_METHOD("get_language"), &TextEdit::get_language); - ClassDB::bind_method(D_METHOD("get_first_non_whitespace_column", "line"), &TextEdit::get_first_non_whitespace_column); - ClassDB::bind_method(D_METHOD("get_indent_level", "line"), &TextEdit::get_indent_level); - ClassDB::bind_method(D_METHOD("set_tab_size", "size"), &TextEdit::set_tab_size); - ClassDB::bind_method(D_METHOD("get_tab_size"), &TextEdit::get_tab_size); - - ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text); - ClassDB::bind_method(D_METHOD("insert_text_at_cursor", "text"), &TextEdit::insert_text_at_cursor); - - ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count); - ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text); - ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); - ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_total_visible_rows); - ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line); - ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextEdit::set_structured_text_bidi_override); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextEdit::get_structured_text_bidi_override); ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextEdit::set_structured_text_bidi_override_options); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextEdit::get_structured_text_bidi_override_options); - ClassDB::bind_method(D_METHOD("center_viewport_to_cursor"), &TextEdit::center_viewport_to_cursor); - ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true), DEFVAL(0)); - - ClassDB::bind_method(D_METHOD("get_caret_draw_pos"), &TextEdit::get_caret_draw_pos); - ClassDB::bind_method(D_METHOD("is_caret_visible"), &TextEdit::is_caret_visible); - ClassDB::bind_method(D_METHOD("cursor_get_column"), &TextEdit::cursor_get_column); - ClassDB::bind_method(D_METHOD("cursor_get_line"), &TextEdit::cursor_get_line); - ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enable"), &TextEdit::cursor_set_blink_enabled); - ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &TextEdit::cursor_get_blink_enabled); - ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &TextEdit::cursor_set_blink_speed); - ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &TextEdit::cursor_get_blink_speed); - ClassDB::bind_method(D_METHOD("cursor_set_block_mode", "enable"), &TextEdit::cursor_set_block_mode); - ClassDB::bind_method(D_METHOD("cursor_is_block_mode"), &TextEdit::cursor_is_block_mode); - - ClassDB::bind_method(D_METHOD("set_mid_grapheme_caret_enabled", "enabled"), &TextEdit::set_mid_grapheme_caret_enabled); - ClassDB::bind_method(D_METHOD("get_mid_grapheme_caret_enabled"), &TextEdit::get_mid_grapheme_caret_enabled); - - ClassDB::bind_method(D_METHOD("set_right_click_moves_caret", "enable"), &TextEdit::set_right_click_moves_caret); - ClassDB::bind_method(D_METHOD("is_right_click_moving_caret"), &TextEdit::is_right_click_moving_caret); - - ClassDB::bind_method(D_METHOD("get_selection_mode"), &TextEdit::get_selection_mode); - ClassDB::bind_method(D_METHOD("set_selection_mode", "mode", "line", "column"), &TextEdit::set_selection_mode, DEFVAL(-1), DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_selection_line"), &TextEdit::get_selection_line); - ClassDB::bind_method(D_METHOD("get_selection_column"), &TextEdit::get_selection_column); + ClassDB::bind_method(D_METHOD("set_tab_size", "size"), &TextEdit::set_tab_size); + ClassDB::bind_method(D_METHOD("get_tab_size"), &TextEdit::get_tab_size); - ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly); - ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly); + // User controls + ClassDB::bind_method(D_METHOD("set_overtype_mode_enabled", "enabled"), &TextEdit::set_overtype_mode_enabled); + ClassDB::bind_method(D_METHOD("is_overtype_mode_enabled"), &TextEdit::is_overtype_mode_enabled); - ClassDB::bind_method(D_METHOD("set_wrap_enabled", "enable"), &TextEdit::set_wrap_enabled); - ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled); + ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled); ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &TextEdit::set_virtual_keyboard_enabled); ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &TextEdit::is_virtual_keyboard_enabled); - ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); - ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); - ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection); + // Text manipulation + ClassDB::bind_method(D_METHOD("clear"), &TextEdit::clear); + + ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text); + ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text); + ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count); + + ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line); + ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); + + ClassDB::bind_method(D_METHOD("get_line_width", "line", "wrap_index"), &TextEdit::get_line_width, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_line_height"), &TextEdit::get_line_height); + + ClassDB::bind_method(D_METHOD("get_indent_level", "line"), &TextEdit::get_indent_level); + ClassDB::bind_method(D_METHOD("get_first_non_whitespace_column", "line"), &TextEdit::get_first_non_whitespace_column); + + ClassDB::bind_method(D_METHOD("swap_lines", "from_line", "to_line"), &TextEdit::swap_lines); + + ClassDB::bind_method(D_METHOD("insert_line_at", "line", "text"), &TextEdit::insert_line_at); + ClassDB::bind_method(D_METHOD("insert_text_at_caret", "text"), &TextEdit::insert_text_at_caret); + + ClassDB::bind_method(D_METHOD("remove_text", "from_line", "from_column", "to_line", "to_column"), &TextEdit::remove_text); + + ClassDB::bind_method(D_METHOD("get_last_unhidden_line"), &TextEdit::get_last_unhidden_line); + ClassDB::bind_method(D_METHOD("get_next_visible_line_offset_from", "line", "visible_amount"), &TextEdit::get_next_visible_line_offset_from); + ClassDB::bind_method(D_METHOD("get_next_visible_line_index_offset_from", "line", "wrap_index", "visible_amount"), &TextEdit::get_next_visible_line_index_offset_from); + + // Overridable actions ClassDB::bind_method(D_METHOD("backspace"), &TextEdit::backspace); - BIND_VMETHOD(MethodInfo("_backspace")); - BIND_VMETHOD(MethodInfo("_handle_unicode_input", PropertyInfo(Variant::INT, "unicode"))) ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut); ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy); ClassDB::bind_method(D_METHOD("paste"), &TextEdit::paste); - ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select); - ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); - ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); - ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor); + BIND_VMETHOD(MethodInfo("_handle_unicode_input", PropertyInfo(Variant::INT, "unicode"))) + BIND_VMETHOD(MethodInfo("_backspace")); - ClassDB::bind_method(D_METHOD("is_selection_active"), &TextEdit::is_selection_active); - ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line); - ClassDB::bind_method(D_METHOD("get_selection_from_column"), &TextEdit::get_selection_from_column); - ClassDB::bind_method(D_METHOD("get_selection_to_line"), &TextEdit::get_selection_to_line); - ClassDB::bind_method(D_METHOD("get_selection_to_column"), &TextEdit::get_selection_to_column); - ClassDB::bind_method(D_METHOD("get_selection_text"), &TextEdit::get_selection_text); - ClassDB::bind_method(D_METHOD("get_word_under_cursor"), &TextEdit::get_word_under_cursor); - ClassDB::bind_method(D_METHOD("search", "key", "flags", "from_line", "from_column"), &TextEdit::_search_bind); + BIND_VMETHOD(MethodInfo("_cut")); + BIND_VMETHOD(MethodInfo("_copy")); + BIND_VMETHOD(MethodInfo("_paste")); + + // Context Menu + BIND_ENUM_CONSTANT(MENU_CUT); + BIND_ENUM_CONSTANT(MENU_COPY); + BIND_ENUM_CONSTANT(MENU_PASTE); + BIND_ENUM_CONSTANT(MENU_CLEAR); + BIND_ENUM_CONSTANT(MENU_SELECT_ALL); + BIND_ENUM_CONSTANT(MENU_UNDO); + BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); + BIND_ENUM_CONSTANT(MENU_DIR_AUTO); + BIND_ENUM_CONSTANT(MENU_DIR_LTR); + BIND_ENUM_CONSTANT(MENU_DIR_RTL); + BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_INSERT_LRM); + BIND_ENUM_CONSTANT(MENU_INSERT_RLM); + BIND_ENUM_CONSTANT(MENU_INSERT_LRE); + BIND_ENUM_CONSTANT(MENU_INSERT_RLE); + BIND_ENUM_CONSTANT(MENU_INSERT_LRO); + BIND_ENUM_CONSTANT(MENU_INSERT_RLO); + BIND_ENUM_CONSTANT(MENU_INSERT_PDF); + BIND_ENUM_CONSTANT(MENU_INSERT_ALM); + BIND_ENUM_CONSTANT(MENU_INSERT_LRI); + BIND_ENUM_CONSTANT(MENU_INSERT_RLI); + BIND_ENUM_CONSTANT(MENU_INSERT_FSI); + BIND_ENUM_CONSTANT(MENU_INSERT_PDI); + BIND_ENUM_CONSTANT(MENU_INSERT_ZWJ); + BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ); + BIND_ENUM_CONSTANT(MENU_INSERT_WJ); + BIND_ENUM_CONSTANT(MENU_INSERT_SHY); + BIND_ENUM_CONSTANT(MENU_MAX); + + /* Versioning */ + ClassDB::bind_method(D_METHOD("begin_complex_operation"), &TextEdit::begin_complex_operation); + ClassDB::bind_method(D_METHOD("end_complex_operation"), &TextEdit::end_complex_operation); ClassDB::bind_method(D_METHOD("undo"), &TextEdit::undo); ClassDB::bind_method(D_METHOD("redo"), &TextEdit::redo); ClassDB::bind_method(D_METHOD("clear_undo_history"), &TextEdit::clear_undo_history); - ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs); - ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs); - ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces); - ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces); + ClassDB::bind_method(D_METHOD("tag_saved_version"), &TextEdit::tag_saved_version); - ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences); - ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled); + ClassDB::bind_method(D_METHOD("get_version"), &TextEdit::get_version); + ClassDB::bind_method(D_METHOD("get_saved_version"), &TextEdit::get_saved_version); + + /* Search */ + BIND_ENUM_CONSTANT(SEARCH_MATCH_CASE); + BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS); + BIND_ENUM_CONSTANT(SEARCH_BACKWARDS); + + ClassDB::bind_method(D_METHOD("set_search_text", "search_text"), &TextEdit::set_search_text); + ClassDB::bind_method(D_METHOD("set_search_flags", "flags"), &TextEdit::set_search_flags); + + ClassDB::bind_method(D_METHOD("search", "text", "flags", "from_line", "from_colum"), &TextEdit::search); + + /* Tooltip */ + ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "object", "callback", "data"), &TextEdit::set_tooltip_request_func); + + /* Mouse */ + ClassDB::bind_method(D_METHOD("get_local_mouse_pos"), &TextEdit::get_local_mouse_pos); + + ClassDB::bind_method(D_METHOD("get_word_at_pos", "position"), &TextEdit::get_word_at_pos); + + ClassDB::bind_method(D_METHOD("get_line_column_at_pos", "position"), &TextEdit::get_line_column_at_pos); + ClassDB::bind_method(D_METHOD("get_minimap_line_at_pos", "position"), &TextEdit::get_minimap_line_at_pos); + + ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor); + + /* Caret. */ + BIND_ENUM_CONSTANT(CARET_TYPE_LINE); + BIND_ENUM_CONSTANT(CARET_TYPE_BLOCK); + + // internal. + ClassDB::bind_method(D_METHOD("_emit_caret_changed"), &TextEdit::_emit_caret_changed); + + ClassDB::bind_method(D_METHOD("set_caret_type", "type"), &TextEdit::set_caret_type); + ClassDB::bind_method(D_METHOD("get_caret_type"), &TextEdit::get_caret_type); + + ClassDB::bind_method(D_METHOD("set_caret_blink_enabled", "enable"), &TextEdit::set_caret_blink_enabled); + ClassDB::bind_method(D_METHOD("is_caret_blink_enabled"), &TextEdit::is_caret_blink_enabled); + + ClassDB::bind_method(D_METHOD("set_caret_blink_speed", "blink_speed"), &TextEdit::set_caret_blink_speed); + ClassDB::bind_method(D_METHOD("get_caret_blink_speed"), &TextEdit::get_caret_blink_speed); + + ClassDB::bind_method(D_METHOD("set_move_caret_on_right_click_enabled", "enable"), &TextEdit::set_move_caret_on_right_click_enabled); + ClassDB::bind_method(D_METHOD("is_move_caret_on_right_click_enabled"), &TextEdit::is_move_caret_on_right_click_enabled); + + ClassDB::bind_method(D_METHOD("set_caret_mid_grapheme_enabled", "enabled"), &TextEdit::set_caret_mid_grapheme_enabled); + ClassDB::bind_method(D_METHOD("is_caret_mid_grapheme_enabled"), &TextEdit::is_caret_mid_grapheme_enabled); + + ClassDB::bind_method(D_METHOD("is_caret_visible"), &TextEdit::is_caret_visible); + ClassDB::bind_method(D_METHOD("get_caret_draw_pos"), &TextEdit::get_caret_draw_pos); + + ClassDB::bind_method(D_METHOD("set_caret_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::set_caret_line, DEFVAL(true), DEFVAL(true), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_caret_line"), &TextEdit::get_caret_line); + + ClassDB::bind_method(D_METHOD("set_caret_column", "column", "adjust_viewport"), &TextEdit::set_caret_column, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_caret_column"), &TextEdit::get_caret_column); + + ClassDB::bind_method(D_METHOD("get_caret_wrap_index"), &TextEdit::get_caret_wrap_index); + + ClassDB::bind_method(D_METHOD("get_word_under_caret"), &TextEdit::get_word_under_caret); + + /* Selection. */ + BIND_ENUM_CONSTANT(SELECTION_MODE_NONE); + BIND_ENUM_CONSTANT(SELECTION_MODE_SHIFT); + BIND_ENUM_CONSTANT(SELECTION_MODE_POINTER); + BIND_ENUM_CONSTANT(SELECTION_MODE_WORD); + BIND_ENUM_CONSTANT(SELECTION_MODE_LINE); + + ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); + ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color); ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color); - ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter); - ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter); + ClassDB::bind_method(D_METHOD("set_selection_mode", "mode", "line", "column"), &TextEdit::set_selection_mode, DEFVAL(-1), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_selection_mode"), &TextEdit::get_selection_mode); + + ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); + ClassDB::bind_method(D_METHOD("select_word_under_caret"), &TextEdit::select_word_under_caret); + ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select); + + ClassDB::bind_method(D_METHOD("has_selection"), &TextEdit::has_selection); + + ClassDB::bind_method(D_METHOD("get_selected_text"), &TextEdit::get_selected_text); + + ClassDB::bind_method(D_METHOD("get_selection_line"), &TextEdit::get_selection_line); + ClassDB::bind_method(D_METHOD("get_selection_column"), &TextEdit::get_selection_column); + + ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line); + ClassDB::bind_method(D_METHOD("get_selection_from_column"), &TextEdit::get_selection_from_column); + ClassDB::bind_method(D_METHOD("get_selection_to_line"), &TextEdit::get_selection_to_line); + ClassDB::bind_method(D_METHOD("get_selection_to_column"), &TextEdit::get_selection_to_column); + + ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); + ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection); + + /* line wrapping. */ + BIND_ENUM_CONSTANT(LINE_WRAPPING_NONE); + BIND_ENUM_CONSTANT(LINE_WRAPPING_BOUNDARY); + + // internal. + ClassDB::bind_method(D_METHOD("_update_wrap_at_column", "force"), &TextEdit::_update_wrap_at_column, DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("set_line_wrapping_mode", "mode"), &TextEdit::set_line_wrapping_mode); + ClassDB::bind_method(D_METHOD("get_line_wrapping_mode"), &TextEdit::get_line_wrapping_mode); + + ClassDB::bind_method(D_METHOD("is_line_wrapped", "line"), &TextEdit::is_line_wrapped); + ClassDB::bind_method(D_METHOD("get_line_wrap_count", "line"), &TextEdit::get_line_wrap_count); + ClassDB::bind_method(D_METHOD("get_line_wrap_index_at_column", "line", "column"), &TextEdit::get_line_wrap_index_at_column); + + ClassDB::bind_method(D_METHOD("get_line_wrapped_text", "line"), &TextEdit::get_line_wrapped_text); + + /* Viewport. */ + // Scolling. + ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled); + ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled); + + ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &TextEdit::set_v_scroll); + ClassDB::bind_method(D_METHOD("get_v_scroll"), &TextEdit::get_v_scroll); + + ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &TextEdit::set_h_scroll); + ClassDB::bind_method(D_METHOD("get_h_scroll"), &TextEdit::get_h_scroll); + + ClassDB::bind_method(D_METHOD("set_scroll_past_end_of_file_enabled", "enable"), &TextEdit::set_scroll_past_end_of_file_enabled); + ClassDB::bind_method(D_METHOD("is_scroll_past_end_of_file_enabled"), &TextEdit::is_scroll_past_end_of_file_enabled); + + ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); + ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); + + ClassDB::bind_method(D_METHOD("get_scroll_pos_for_line", "line", "wrap_index"), &TextEdit::get_scroll_pos_for_line, DEFVAL(0)); + + // Visible lines. + ClassDB::bind_method(D_METHOD("set_line_as_first_visible", "line", "wrap_index"), &TextEdit::set_line_as_first_visible, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_first_visible_line"), &TextEdit::get_first_visible_line); + + ClassDB::bind_method(D_METHOD("set_line_as_center_visible", "line", "wrap_index"), &TextEdit::set_line_as_center_visible, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("set_line_as_last_visible", "line", "wrap_index"), &TextEdit::set_line_as_last_visible, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_last_full_visible_line"), &TextEdit::get_last_full_visible_line); + ClassDB::bind_method(D_METHOD("get_last_full_visible_line_wrap_index"), &TextEdit::get_last_full_visible_line_wrap_index); + + ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_visible_line_count); + ClassDB::bind_method(D_METHOD("get_total_visible_line_count"), &TextEdit::get_total_visible_line_count); + + // Auto adjust + ClassDB::bind_method(D_METHOD("adjust_viewport_to_caret"), &TextEdit::adjust_viewport_to_caret); + ClassDB::bind_method(D_METHOD("center_viewport_to_caret"), &TextEdit::center_viewport_to_caret); + + // Minimap + ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap); + ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap); + + ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width); + ClassDB::bind_method(D_METHOD("get_minimap_width"), &TextEdit::get_minimap_width); + + ClassDB::bind_method(D_METHOD("get_minimap_visible_lines"), &TextEdit::get_minimap_visible_lines); /* Gutters. */ BIND_ENUM_CONSTANT(GUTTER_TYPE_STRING); @@ -5526,108 +4639,321 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_line_background_color", "line", "color"), &TextEdit::set_line_background_color); ClassDB::bind_method(D_METHOD("get_line_background_color", "line"), &TextEdit::get_line_background_color); + /* Syntax Highlighting. */ + ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter); + ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter); + + /* Visual. */ ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line); ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled); - ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled); - ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled); - ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); - ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); - ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &TextEdit::set_v_scroll); - ClassDB::bind_method(D_METHOD("get_v_scroll"), &TextEdit::get_v_scroll); - ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &TextEdit::set_h_scroll); - ClassDB::bind_method(D_METHOD("get_h_scroll"), &TextEdit::get_h_scroll); + ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences); + ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled); + + ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &TextEdit::get_draw_control_chars); + ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &TextEdit::set_draw_control_chars); + + ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs); + ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs); + + ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces); + ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces); - ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option); ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu); ClassDB::bind_method(D_METHOD("is_menu_visible"), &TextEdit::is_menu_visible); + ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option); - ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap); - ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap); - ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width); - ClassDB::bind_method(D_METHOD("get_minimap_width"), &TextEdit::get_minimap_width); - + /* Inspector */ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); + ADD_GROUP("Scroll", "scroll_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_past_end_of_file"), "set_scroll_past_end_of_file_enabled", "is_scroll_past_end_of_file_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); + ADD_GROUP("Minimap", "minimap_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap"); ADD_PROPERTY(PropertyInfo(Variant::INT, "minimap_width"), "set_minimap_width", "get_minimap_width"); ADD_GROUP("Caret", "caret_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_mid_grapheme_caret_enabled", "get_mid_grapheme_caret_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_type", PROPERTY_HINT_ENUM, "Line,Block"), "set_caret_type", "get_caret_type"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_move_on_right_click"), "set_move_caret_on_right_click_enabled", "is_move_caret_on_right_click_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); - ADD_SIGNAL(MethodInfo("cursor_changed")); + /* Signals */ + /* Core. */ ADD_SIGNAL(MethodInfo("text_changed")); ADD_SIGNAL(MethodInfo("lines_edited_from", PropertyInfo(Variant::INT, "from_line"), PropertyInfo(Variant::INT, "to_line"))); + + /* Caret. */ + ADD_SIGNAL(MethodInfo("caret_changed")); + + /* Gutters. */ ADD_SIGNAL(MethodInfo("gutter_clicked", PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "gutter"))); ADD_SIGNAL(MethodInfo("gutter_added")); ADD_SIGNAL(MethodInfo("gutter_removed")); - BIND_ENUM_CONSTANT(MENU_CUT); - BIND_ENUM_CONSTANT(MENU_COPY); - BIND_ENUM_CONSTANT(MENU_PASTE); - BIND_ENUM_CONSTANT(MENU_CLEAR); - BIND_ENUM_CONSTANT(MENU_SELECT_ALL); - BIND_ENUM_CONSTANT(MENU_UNDO); - BIND_ENUM_CONSTANT(MENU_REDO); - BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); - BIND_ENUM_CONSTANT(MENU_DIR_AUTO); - BIND_ENUM_CONSTANT(MENU_DIR_LTR); - BIND_ENUM_CONSTANT(MENU_DIR_RTL); - BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); - BIND_ENUM_CONSTANT(MENU_INSERT_LRM); - BIND_ENUM_CONSTANT(MENU_INSERT_RLM); - BIND_ENUM_CONSTANT(MENU_INSERT_LRE); - BIND_ENUM_CONSTANT(MENU_INSERT_RLE); - BIND_ENUM_CONSTANT(MENU_INSERT_LRO); - BIND_ENUM_CONSTANT(MENU_INSERT_RLO); - BIND_ENUM_CONSTANT(MENU_INSERT_PDF); - BIND_ENUM_CONSTANT(MENU_INSERT_ALM); - BIND_ENUM_CONSTANT(MENU_INSERT_LRI); - BIND_ENUM_CONSTANT(MENU_INSERT_RLI); - BIND_ENUM_CONSTANT(MENU_INSERT_FSI); - BIND_ENUM_CONSTANT(MENU_INSERT_PDI); - BIND_ENUM_CONSTANT(MENU_INSERT_ZWJ); - BIND_ENUM_CONSTANT(MENU_INSERT_ZWNJ); - BIND_ENUM_CONSTANT(MENU_INSERT_WJ); - BIND_ENUM_CONSTANT(MENU_INSERT_SHY); - BIND_ENUM_CONSTANT(MENU_MAX); - + /* Settings. */ GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3); ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::FLOAT, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers. GLOBAL_DEF("gui/common/text_edit_undo_stack_max_size", 1024); ProjectSettings::get_singleton()->set_custom_property_info("gui/common/text_edit_undo_stack_max_size", PropertyInfo(Variant::INT, "gui/common/text_edit_undo_stack_max_size", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers. } -void TextEdit::_ensure_menu() { +bool TextEdit::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + double value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + text.set_font_features(opentype_features); + text.invalidate_all(); + update(); + } + } else { + if ((double)opentype_features[tag] != value) { + opentype_features[tag] = value; + text.set_font_features(opentype_features); + text.invalidate_all(); + ; + update(); + } + } + notify_property_list_changed(); + return true; + } + + return false; +} + +bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); +} + +/* Internal API for CodeEdit. */ +// Line hiding. +void TextEdit::_set_hiding_enabled(bool p_enabled) { + if (!p_enabled) { + _unhide_all_lines(); + } + hiding_enabled = p_enabled; + update(); +} + +bool TextEdit::_is_hiding_enabled() const { + return hiding_enabled; +} + +bool TextEdit::_is_line_hidden(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), false); + return text.is_hidden(p_line); +} + +void TextEdit::_unhide_all_lines() { + for (int i = 0; i < text.size(); i++) { + text.set_hidden(i, false); + } + _update_scrollbars(); + update(); +} + +void TextEdit::_set_line_as_hidden(int p_line, bool p_hidden) { + ERR_FAIL_INDEX(p_line, text.size()); + if (_is_hiding_enabled() || !p_hidden) { + text.set_hidden(p_line, p_hidden); + } + update(); +} + +// Symbol lookup. +void TextEdit::_set_symbol_lookup_word(const String &p_symbol) { + lookup_symbol_word = p_symbol; + update(); +} + +/* Text manipulation */ + +// Overridable actions +void TextEdit::_handle_unicode_input(const uint32_t p_unicode) { + if (!editable) { + return; + } + + bool had_selection = has_selection(); + if (had_selection) { + begin_complex_operation(); + delete_selection(); + } + + /* Remove the old character if in insert mode and no selection. */ + if (overtype_mode && !had_selection) { + begin_complex_operation(); + + /* Make sure we don't try and remove empty space. */ + int cl = get_caret_line(); + int cc = get_caret_column(); + if (cc < get_line(cl).length()) { + _remove_text(cl, cc, cl, cc + 1); + } + } + + const char32_t chr[2] = { (char32_t)p_unicode, 0 }; + insert_text_at_caret(chr); + + if ((overtype_mode && !had_selection) || (had_selection)) { + end_complex_operation(); + } +} + +void TextEdit::_backspace() { + if (!editable) { + return; + } + + int cc = get_caret_column(); + int cl = get_caret_line(); + + if (cc == 0 && cl == 0) { + return; + } + + if (has_selection()) { + delete_selection(); + return; + } + + int prev_line = cc ? cl : cl - 1; + int prev_column = cc ? (cc - 1) : (text[cl - 1].length()); + + merge_gutters(cl, prev_line); + + if (_is_line_hidden(cl)) { + _set_line_as_hidden(prev_line, true); + } + _remove_text(prev_line, prev_column, cl, cc); + + set_caret_line(prev_line, false, true); + set_caret_column(prev_column); +} + +void TextEdit::_cut() { + if (!editable) { + return; + } + + if (has_selection()) { + DisplayServer::get_singleton()->clipboard_set(get_selected_text()); + delete_selection(); + cut_copy_line = ""; + return; + } + + int cl = get_caret_line(); + + String clipboard = text[cl]; + DisplayServer::get_singleton()->clipboard_set(clipboard); + set_caret_line(cl); + set_caret_column(0); + + if (cl == 0 && get_line_count() > 1) { + _remove_text(cl, 0, cl + 1, 0); + } else { + _remove_text(cl, 0, cl, text[cl].length()); + backspace(); + set_caret_line(get_caret_line() + 1); + } + + cut_copy_line = clipboard; +} + +void TextEdit::_copy() { + if (has_selection()) { + DisplayServer::get_singleton()->clipboard_set(get_selected_text()); + cut_copy_line = ""; + return; + } + + int cl = get_caret_line(); + if (text[cl].length() != 0) { + String clipboard = _base_get_text(cl, 0, cl, text[cl].length()); + DisplayServer::get_singleton()->clipboard_set(clipboard); + cut_copy_line = clipboard; + } +} + +void TextEdit::_paste() { + if (!editable) { + return; + } + + String clipboard = DisplayServer::get_singleton()->clipboard_get(); + + begin_complex_operation(); + if (has_selection()) { + delete_selection(); + } else if (!cut_copy_line.is_empty() && cut_copy_line == clipboard) { + set_caret_column(0); + String ins = "\n"; + clipboard += ins; + } + + insert_text_at_caret(clipboard); + end_complex_operation(); +} + +/* Text. */ +// Context menu. +void TextEdit::_generate_context_menu() { if (!menu) { menu = memnew(PopupMenu); add_child(menu); @@ -5669,18 +4995,18 @@ void TextEdit::_ensure_menu() { // Reorganize context menu. menu->clear(); - if (!readonly) { + if (editable) { menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : 0); } menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : 0); - if (!readonly) { + if (editable) { menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : 0); } menu->add_separator(); if (is_selecting_enabled()) { menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : 0); } - if (!readonly) { + if (editable) { menu->add_item(RTR("Clear"), MENU_CLEAR); menu->add_separator(); menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : 0); @@ -5691,7 +5017,7 @@ void TextEdit::_ensure_menu() { menu->add_separator(); menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC); menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); - if (!readonly) { + if (editable) { menu->add_submenu_item(RTR("Insert control character"), "CTLMenu"); } menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); @@ -5700,6 +5026,863 @@ void TextEdit::_ensure_menu() { menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); } +int TextEdit::_get_menu_action_accelerator(const String &p_action) { + const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); + if (!events) { + return 0; + } + + // Use first event in the list for the accelerator. + const List<Ref<InputEvent>>::Element *first_event = events->front(); + if (!first_event) { + return 0; + } + + const Ref<InputEventKey> event = first_event->get(); + if (event.is_null()) { + return 0; + } + + // Use physical keycode if non-zero + if (event->get_physical_keycode() != 0) { + return event->get_physical_keycode_with_modifiers(); + } else { + return event->get_keycode_with_modifiers(); + } +} + +/* Versioning */ +void TextEdit::_push_current_op() { + if (current_op.type == TextOperation::TYPE_NONE) { + return; // Nothing to do. + } + + if (next_operation_is_complex) { + current_op.chain_forward = true; + next_operation_is_complex = false; + } + + undo_stack.push_back(current_op); + current_op.type = TextOperation::TYPE_NONE; + current_op.text = ""; + current_op.chain_forward = false; + + if (undo_stack.size() > undo_stack_max_size) { + undo_stack.pop_front(); + } +} + +void TextEdit::_do_text_op(const TextOperation &p_op, bool p_reverse) { + ERR_FAIL_COND(p_op.type == TextOperation::TYPE_NONE); + + bool insert = p_op.type == TextOperation::TYPE_INSERT; + if (p_reverse) { + insert = !insert; + } + + if (insert) { + int check_line; + int check_column; + _base_insert_text(p_op.from_line, p_op.from_column, p_op.text, check_line, check_column); + ERR_FAIL_COND(check_line != p_op.to_line); // BUG. + ERR_FAIL_COND(check_column != p_op.to_column); // BUG. + } else { + _base_remove_text(p_op.from_line, p_op.from_column, p_op.to_line, p_op.to_column); + } +} + +void TextEdit::_clear_redo() { + if (undo_stack_pos == nullptr) { + return; // Nothing to clear. + } + + _push_current_op(); + + while (undo_stack_pos) { + List<TextOperation>::Element *elem = undo_stack_pos; + undo_stack_pos = undo_stack_pos->next(); + undo_stack.erase(elem); + } +} + +/* Search */ +int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const { + int col = -1; + + if (p_key.length() > 0 && p_search.length() > 0) { + if (p_from_column < 0 || p_from_column > p_search.length()) { + p_from_column = 0; + } + + while (col == -1 && p_from_column <= p_search.length()) { + if (p_search_flags & SEARCH_MATCH_CASE) { + col = p_search.find(p_key, p_from_column); + } else { + col = p_search.findn(p_key, p_from_column); + } + + // Whole words only. + if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) { + p_from_column = col; + + if (col > 0 && _is_text_char(p_search[col - 1])) { + col = -1; + } else if ((col + p_key.length()) < p_search.length() && _is_text_char(p_search[col + p_key.length()])) { + col = -1; + } + } + + p_from_column += 1; + } + } + return col; +} + +/* Mouse */ +int TextEdit::_get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + p_wrap_index = MIN(p_wrap_index, text.get_line_data(p_line)->get_line_count() - 1); + + RID text_rid = text.get_line_data(p_line)->get_line_rid(p_wrap_index); + if (is_layout_rtl()) { + p_px = TS->shaped_text_get_size(text_rid).x - p_px; + } + return TS->shaped_text_hit_test_position(text_rid, p_px); +} + +/* Caret */ +void TextEdit::_emit_caret_changed() { + emit_signal(SNAME("caret_changed")); + caret_pos_dirty = false; +} + +void TextEdit::_reset_caret_blink_timer() { + if (!caret_blink_enabled) { + return; + } + + draw_caret = true; + if (has_focus()) { + caret_blink_timer->stop(); + caret_blink_timer->start(); + update(); + } +} + +void TextEdit::_toggle_draw_caret() { + draw_caret = !draw_caret; + if (is_visible_in_tree() && has_focus() && window_has_focus) { + update(); + } +} + +int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + + int row = 0; + Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line); + for (int i = 0; i < rows2.size(); i++) { + if ((p_char >= rows2[i].x) && (p_char < rows2[i].y)) { + row = i; + break; + } + } + + Rect2 l_caret, t_caret; + TextServer::Direction l_dir, t_dir; + RID text_rid = text.get_line_data(p_line)->get_line_rid(row); + TS->shaped_text_get_carets(text_rid, caret.column, l_caret, l_dir, t_caret, t_dir); + if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) { + return l_caret.position.x; + } else { + return t_caret.position.x; + } +} + +/* Selection */ +void TextEdit::_click_selection_held() { + // Warning: is_mouse_button_pressed(MOUSE_BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD + // and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem. + // I'm unsure if there's an actual fix that doesn't have a ton of side effects. + if (Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT) && selection.selecting_mode != SelectionMode::SELECTION_MODE_NONE) { + switch (selection.selecting_mode) { + case SelectionMode::SELECTION_MODE_POINTER: { + _update_selection_mode_pointer(); + } break; + case SelectionMode::SELECTION_MODE_WORD: { + _update_selection_mode_word(); + } break; + case SelectionMode::SELECTION_MODE_LINE: { + _update_selection_mode_line(); + } break; + default: { + break; + } + } + } else { + click_select_held->stop(); + } +} + +void TextEdit::_update_selection_mode_pointer() { + dragging_selection = true; + Point2 mp = get_local_mouse_pos(); + + Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + int line = pos.y; + int col = pos.x; + + select(selection.selecting_line, selection.selecting_column, line, col); + + set_caret_line(line, false); + set_caret_column(col); + update(); + + click_select_held->start(); +} + +void TextEdit::_update_selection_mode_word() { + dragging_selection = true; + Point2 mp = get_local_mouse_pos(); + + Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + int line = pos.y; + int col = pos.x; + + int caret_pos = CLAMP(col, 0, text[line].length()); + int beg = caret_pos; + int end = beg; + Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); + for (int i = 0; i < words.size(); i++) { + if (words[i].x < caret_pos && words[i].y > caret_pos) { + beg = words[i].x; + end = words[i].y; + break; + } + } + + /* Initial selection. */ + if (!selection.active) { + select(line, beg, line, end); + selection.selecting_column = beg; + selection.selected_word_beg = beg; + selection.selected_word_end = end; + selection.selected_word_origin = beg; + set_caret_line(selection.to_line, false); + set_caret_column(selection.to_column); + } else { + if ((col <= selection.selected_word_origin && line == selection.selecting_line) || line < selection.selecting_line) { + selection.selecting_column = selection.selected_word_end; + select(line, beg, selection.selecting_line, selection.selected_word_end); + set_caret_line(selection.from_line, false); + set_caret_column(selection.from_column); + } else { + selection.selecting_column = selection.selected_word_beg; + select(selection.selecting_line, selection.selected_word_beg, line, end); + set_caret_line(selection.to_line, false); + set_caret_column(selection.to_column); + } + } + + update(); + + click_select_held->start(); +} + +void TextEdit::_update_selection_mode_line() { + dragging_selection = true; + Point2 mp = get_local_mouse_pos(); + + Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + int line = pos.y; + int col = pos.x; + + col = 0; + if (line < selection.selecting_line) { + /* Caret is above us. */ + set_caret_line(line - 1, false); + selection.selecting_column = text[selection.selecting_line].length(); + } else { + /* Caret is below us. */ + set_caret_line(line + 1, false); + selection.selecting_column = 0; + col = text[line].length(); + } + set_caret_column(0); + + select(selection.selecting_line, selection.selecting_column, line, col); + update(); + + click_select_held->start(); +} + +void TextEdit::_pre_shift_selection() { + if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) { + selection.selecting_line = caret.line; + selection.selecting_column = caret.column; + selection.active = true; + } + + selection.selecting_mode = SelectionMode::SELECTION_MODE_SHIFT; +} + +void TextEdit::_post_shift_selection() { + if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) { + select(selection.selecting_line, selection.selecting_column, caret.line, caret.column); + update(); + } + + selection.selecting_text = true; +} + +/* Line Wrapping */ +void TextEdit::_update_wrap_at_column(bool p_force) { + int new_wrap_at = get_size().width - style_normal->get_minimum_size().width - gutters_width - gutter_padding; + if (draw_minimap) { + new_wrap_at -= minimap_width; + } + if (v_scroll->is_visible_in_tree()) { + new_wrap_at -= v_scroll->get_combined_minimum_size().width; + } + /* Give it a little more space. */ + new_wrap_at -= wrap_right_offset; + + if ((wrap_at_column != new_wrap_at) || p_force) { + wrap_at_column = new_wrap_at; + if (line_wrapping_mode) { + text.set_width(wrap_at_column); + } else { + text.set_width(-1); + } + text.invalidate_all_lines(); + } + + _update_caret_wrap_offset(); +} + +void TextEdit::_update_caret_wrap_offset() { + int first_vis_line = get_first_visible_line(); + if (is_line_wrapped(first_vis_line)) { + caret.wrap_ofs = MIN(caret.wrap_ofs, get_line_wrap_count(first_vis_line)); + } else { + caret.wrap_ofs = 0; + } + set_line_as_first_visible(caret.line_ofs, caret.wrap_ofs); +} + +/* Viewport. */ +void TextEdit::_update_scrollbars() { + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_begin(Point2(size.width - vmin.width, style_normal->get_margin(SIDE_TOP))); + v_scroll->set_end(Point2(size.width, size.height - style_normal->get_margin(SIDE_TOP) - style_normal->get_margin(SIDE_BOTTOM))); + + h_scroll->set_begin(Point2(0, size.height - hmin.height)); + h_scroll->set_end(Point2(size.width - vmin.width, size.height)); + + int visible_rows = get_visible_line_count(); + int total_rows = get_total_visible_line_count(); + if (scroll_past_end_of_file_enabled) { + total_rows += visible_rows - 1; + } + + int visible_width = size.width - style_normal->get_minimum_size().width; + int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding; + + if (draw_minimap) { + total_width += minimap_width; + } + + updating_scrolls = true; + + if (total_rows > visible_rows) { + v_scroll->show(); + v_scroll->set_max(total_rows + _get_visible_lines_offset()); + v_scroll->set_page(visible_rows + _get_visible_lines_offset()); + if (smooth_scroll_enabled) { + v_scroll->set_step(0.25); + } else { + v_scroll->set_step(1); + } + set_v_scroll(get_v_scroll()); + + } else { + caret.line_ofs = 0; + caret.wrap_ofs = 0; + v_scroll->set_value(0); + v_scroll->hide(); + } + + if (total_width > visible_width && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) { + h_scroll->show(); + h_scroll->set_max(total_width); + h_scroll->set_page(visible_width); + if (caret.x_ofs > (total_width - visible_width)) { + caret.x_ofs = (total_width - visible_width); + } + if (fabs(h_scroll->get_value() - (double)caret.x_ofs) >= 1) { + h_scroll->set_value(caret.x_ofs); + } + + } else { + caret.x_ofs = 0; + h_scroll->set_value(0); + h_scroll->hide(); + } + + updating_scrolls = false; +} + +int TextEdit::_get_control_height() const { + int control_height = get_size().height; + control_height -= style_normal->get_minimum_size().height; + if (h_scroll->is_visible_in_tree()) { + control_height -= h_scroll->get_size().height; + } + return control_height; +} + +void TextEdit::_v_scroll_input() { + scrolling = false; + minimap_clicked = false; +} + +void TextEdit::_scroll_moved(double p_to_val) { + if (updating_scrolls) { + return; + } + + if (h_scroll->is_visible_in_tree()) { + caret.x_ofs = h_scroll->get_value(); + } + if (v_scroll->is_visible_in_tree()) { + // Set line ofs and wrap ofs. + int v_scroll_i = floor(get_v_scroll()); + int sc = 0; + int n_line; + for (n_line = 0; n_line < text.size(); n_line++) { + if (!_is_line_hidden(n_line)) { + sc++; + sc += get_line_wrap_count(n_line); + if (sc > v_scroll_i) { + break; + } + } + } + n_line = MIN(n_line, text.size() - 1); + int line_wrap_amount = get_line_wrap_count(n_line); + int wi = line_wrap_amount - (sc - v_scroll_i - 1); + wi = CLAMP(wi, 0, line_wrap_amount); + + caret.line_ofs = n_line; + caret.wrap_ofs = wi; + } + update(); +} + +double TextEdit::_get_visible_lines_offset() const { + double total = _get_control_height(); + total /= (double)get_line_height(); + total = total - floor(total); + total = -CLAMP(total, 0.001, 1) + 1; + return total; +} + +double TextEdit::_get_v_scroll_offset() const { + double val = get_v_scroll() - floor(get_v_scroll()); + return CLAMP(val, 0, 1); +} + +void TextEdit::_scroll_up(real_t p_delta) { + if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(-p_delta)) { + scrolling = false; + minimap_clicked = false; + } + + if (scrolling) { + target_v_scroll = (target_v_scroll - p_delta); + } else { + target_v_scroll = (get_v_scroll() - p_delta); + } + + if (smooth_scroll_enabled) { + if (target_v_scroll <= 0) { + target_v_scroll = 0; + } + if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { + v_scroll->set_value(target_v_scroll); + } else { + scrolling = true; + set_physics_process_internal(true); + } + } else { + set_v_scroll(target_v_scroll); + } +} + +void TextEdit::_scroll_down(real_t p_delta) { + if (scrolling && smooth_scroll_enabled && SGN(target_v_scroll - v_scroll->get_value()) != SGN(p_delta)) { + scrolling = false; + minimap_clicked = false; + } + + if (scrolling) { + target_v_scroll = (target_v_scroll + p_delta); + } else { + target_v_scroll = (get_v_scroll() + p_delta); + } + + if (smooth_scroll_enabled) { + int max_v_scroll = round(v_scroll->get_max() - v_scroll->get_page()); + if (target_v_scroll > max_v_scroll) { + target_v_scroll = max_v_scroll; + } + if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { + v_scroll->set_value(target_v_scroll); + } else { + scrolling = true; + set_physics_process_internal(true); + } + } else { + set_v_scroll(target_v_scroll); + } +} + +void TextEdit::_scroll_lines_up() { + scrolling = false; + minimap_clicked = false; + + // Adjust the vertical scroll. + set_v_scroll(get_v_scroll() - 1); + + // Adjust the caret to viewport. + if (!selection.active) { + int cur_line = caret.line; + int cur_wrap = get_caret_wrap_index(); + int last_vis_line = get_last_full_visible_line(); + int last_vis_wrap = get_last_full_visible_line_wrap_index(); + + if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) { + set_caret_line(last_vis_line, false, false, last_vis_wrap); + } + } +} + +void TextEdit::_scroll_lines_down() { + scrolling = false; + minimap_clicked = false; + + // Adjust the vertical scroll. + set_v_scroll(get_v_scroll() + 1); + + // Adjust the caret to viewport. + if (!selection.active) { + int cur_line = caret.line; + int cur_wrap = get_caret_wrap_index(); + int first_vis_line = get_first_visible_line(); + int first_vis_wrap = caret.wrap_ofs; + + if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) { + set_caret_line(first_vis_line, false, false, first_vis_wrap); + } + } +} + +// Minimap +void TextEdit::_update_minimap_click() { + Point2 mp = get_local_mouse_pos(); + + int xmargin_end = get_size().width - style_normal->get_margin(SIDE_RIGHT); + if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) { + minimap_clicked = false; + return; + } + minimap_clicked = true; + dragging_minimap = true; + + int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y)); + + if (row >= get_first_visible_line() && (row < get_last_full_visible_line() || row >= (text.size() - 1))) { + minimap_scroll_ratio = v_scroll->get_as_ratio(); + minimap_scroll_click_pos = mp.y; + can_drag_minimap = true; + return; + } + + Point2i next_line = get_next_visible_line_index_offset_from(row, 0, -get_visible_line_count() / 2); + int first_line = row - next_line.x + 1; + double delta = get_scroll_pos_for_line(first_line, next_line.y) - get_v_scroll(); + if (delta < 0) { + _scroll_up(-delta); + } else { + _scroll_down(delta); + } +} + +void TextEdit::_update_minimap_drag() { + if (!can_drag_minimap) { + return; + } + + int control_height = _get_control_height(); + int scroll_height = v_scroll->get_max() * (minimap_char_size.y + minimap_line_spacing); + if (control_height > scroll_height) { + control_height = scroll_height; + } + + Point2 mp = get_local_mouse_pos(); + + double diff = (mp.y - minimap_scroll_click_pos) / control_height; + v_scroll->set_as_ratio(minimap_scroll_ratio + diff); +} + +/* Gutters. */ +void TextEdit::_update_gutter_width() { + gutters_width = 0; + for (int i = 0; i < gutters.size(); i++) { + if (gutters[i].draw) { + gutters_width += gutters[i].width; + } + } + if (gutters_width > 0) { + gutter_padding = 2; + } + update(); +} + +/* Syntax highlighting. */ +Dictionary TextEdit::_get_line_syntax_highlighting(int p_line) { + return syntax_highlighter.is_null() && !setting_text ? Dictionary() : syntax_highlighter->get_line_syntax_highlighting(p_line); +} + +/*** Super internal Core API. Everything builds on it. ***/ + +void TextEdit::_text_changed_emit() { + emit_signal(SNAME("text_changed")); + text_changed_dirty = false; +} + +void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) { + if (!setting_text && idle_detect->is_inside_tree()) { + idle_detect->start(); + } + + if (undo_enabled) { + _clear_redo(); + } + + int retline, retchar; + _base_insert_text(p_line, p_char, p_text, retline, retchar); + if (r_end_line) { + *r_end_line = retline; + } + if (r_end_char) { + *r_end_char = retchar; + } + + if (!undo_enabled) { + return; + } + + /* UNDO!! */ + TextOperation op; + op.type = TextOperation::TYPE_INSERT; + op.from_line = p_line; + op.from_column = p_char; + op.to_line = retline; + op.to_column = retchar; + op.text = p_text; + op.version = ++version; + op.chain_forward = false; + op.chain_backward = false; + + // See if it should just be set as current op. + if (current_op.type != op.type) { + op.prev_version = get_version(); + _push_current_op(); + current_op = op; + + return; // Set as current op, return. + } + // See if it can be merged. + if (current_op.to_line != p_line || current_op.to_column != p_char) { + op.prev_version = get_version(); + _push_current_op(); + current_op = op; + return; // Set as current op, return. + } + // Merge current op. + + current_op.text += p_text; + current_op.to_column = retchar; + current_op.to_line = retline; + current_op.version = op.version; +} + +void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { + if (!setting_text && idle_detect->is_inside_tree()) { + idle_detect->start(); + } + + String text; + if (undo_enabled) { + _clear_redo(); + text = _base_get_text(p_from_line, p_from_column, p_to_line, p_to_column); + } + + _base_remove_text(p_from_line, p_from_column, p_to_line, p_to_column); + + if (!undo_enabled) { + return; + } + + /* UNDO! */ + TextOperation op; + op.type = TextOperation::TYPE_REMOVE; + op.from_line = p_from_line; + op.from_column = p_from_column; + op.to_line = p_to_line; + op.to_column = p_to_column; + op.text = text; + op.version = ++version; + op.chain_forward = false; + op.chain_backward = false; + + // See if it should just be set as current op. + if (current_op.type != op.type) { + op.prev_version = get_version(); + _push_current_op(); + current_op = op; + return; // Set as current op, return. + } + // See if it can be merged. + if (current_op.from_line == p_to_line && current_op.from_column == p_to_column) { + // Backspace or similar. + current_op.text = text + current_op.text; + current_op.from_line = p_from_line; + current_op.from_column = p_from_column; + return; // Update current op. + } + + op.prev_version = get_version(); + _push_current_op(); + current_op = op; +} + +void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, int &r_end_line, int &r_end_column) { + // Save for undo. + ERR_FAIL_INDEX(p_line, text.size()); + ERR_FAIL_COND(p_char < 0); + + /* STEP 1: Remove \r from source text and separate in substrings. */ + + Vector<String> substrings = p_text.replace("\r", "").split("\n"); + + // Is this just a new empty line? + bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n"; + + /* STEP 2: Add spaces if the char is greater than the end of the line. */ + while (p_char > text[p_line].length()) { + text.set(p_line, text[p_line] + String::chr(' '), structured_text_parser(st_parser, st_args, text[p_line] + String::chr(' '))); + } + + /* STEP 3: Separate dest string in pre and post text. */ + + String preinsert_text = text[p_line].substr(0, p_char); + String postinsert_text = text[p_line].substr(p_char, text[p_line].size()); + + for (int j = 0; j < substrings.size(); j++) { + // Insert the substrings. + + if (j == 0) { + text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j])); + } else { + text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j])); + } + + if (j == substrings.size() - 1) { + text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text)); + } + } + + if (shift_first_line) { + text.move_gutters(p_line, p_line + 1); + text.set_hidden(p_line + 1, text.is_hidden(p_line)); + + text.set_hidden(p_line, false); + } + + text.invalidate_cache(p_line); + + r_end_line = p_line + substrings.size() - 1; + r_end_column = text[r_end_line].length() - postinsert_text.length(); + + TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column); + if (dir != TextServer::DIRECTION_AUTO) { + input_direction = (TextDirection)dir; + } + + if (!text_changed_dirty && !setting_text) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); + } + text_changed_dirty = true; + } + emit_signal(SNAME("lines_edited_from"), p_line, r_end_line); +} + +String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const { + ERR_FAIL_INDEX_V(p_from_line, text.size(), String()); + ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, String()); + ERR_FAIL_INDEX_V(p_to_line, text.size(), String()); + ERR_FAIL_INDEX_V(p_to_column, text[p_to_line].length() + 1, String()); + ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'. + ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'. + + String ret; + + for (int i = p_from_line; i <= p_to_line; i++) { + int begin = (i == p_from_line) ? p_from_column : 0; + int end = (i == p_to_line) ? p_to_column : text[i].length(); + + if (i > p_from_line) { + ret += "\n"; + } + ret += text[i].substr(begin, end - begin); + } + + return ret; +} + +void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { + ERR_FAIL_INDEX(p_from_line, text.size()); + ERR_FAIL_INDEX(p_from_column, text[p_from_line].length() + 1); + ERR_FAIL_INDEX(p_to_line, text.size()); + ERR_FAIL_INDEX(p_to_column, text[p_to_line].length() + 1); + ERR_FAIL_COND(p_to_line < p_from_line); // 'from > to'. + ERR_FAIL_COND(p_to_line == p_from_line && p_to_column < p_from_column); // 'from > to'. + + String pre_text = text[p_from_line].substr(0, p_from_column); + String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length()); + + for (int i = p_from_line; i < p_to_line; i++) { + text.remove(p_from_line + 1); + } + text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text)); + + text.invalidate_cache(p_from_line); + + if (!text_changed_dirty && !setting_text) { + if (is_inside_tree()) { + MessageQueue::get_singleton()->push_call(this, "_text_changed_emit"); + } + text_changed_dirty = true; + } + emit_signal(SNAME("lines_edited_from"), p_to_line, p_from_line); +} + TextEdit::TextEdit() { clear(); set_focus_mode(FOCUS_ALL); @@ -5720,11 +5903,18 @@ TextEdit::TextEdit() { v_scroll->connect("scrolling", callable_mp(this, &TextEdit::_v_scroll_input)); + /* Caret. */ caret_blink_timer = memnew(Timer); add_child(caret_blink_timer); caret_blink_timer->set_wait_time(0.65); caret_blink_timer->connect("timeout", callable_mp(this, &TextEdit::_toggle_draw_caret)); - cursor_set_blink_enabled(false); + set_caret_blink_enabled(false); + + /* Selection. */ + click_select_held = memnew(Timer); + add_child(click_select_held); + click_select_held->set_wait_time(0.05); + click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held)); idle_detect = memnew(Timer); add_child(idle_detect); @@ -5732,21 +5922,7 @@ TextEdit::TextEdit() { idle_detect->set_wait_time(GLOBAL_GET("gui/timers/text_edit_idle_detect_sec")); idle_detect->connect("timeout", callable_mp(this, &TextEdit::_push_current_op)); - click_select_held = memnew(Timer); - add_child(click_select_held); - click_select_held->set_wait_time(0.05); - click_select_held->connect("timeout", callable_mp(this, &TextEdit::_click_selection_held)); - undo_stack_max_size = GLOBAL_GET("gui/common/text_edit_undo_stack_max_size"); - set_readonly(false); -} - -TextEdit::~TextEdit() { -} - -/////////////////////////////////////////////////////////////////////////////// - -Dictionary TextEdit::_get_line_syntax_highlighting(int p_line) { - return syntax_highlighter.is_null() && !setting_text ? Dictionary() : syntax_highlighter->get_line_syntax_highlighting(p_line); + set_editable(true); } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index c5853786cf..5d05e07b5d 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -42,12 +42,13 @@ class TextEdit : public Control { GDCLASS(TextEdit, Control); public: - enum GutterType { - GUTTER_TYPE_STRING, - GUTTER_TYPE_ICON, - GUTTER_TYPE_CUSTOM + /* Caret. */ + enum CaretType { + CARET_TYPE_LINE, + CARET_TYPE_BLOCK }; + /* Selection */ enum SelectionMode { SELECTION_MODE_NONE, SELECTION_MODE_SHIFT, @@ -56,6 +57,60 @@ public: SELECTION_MODE_LINE }; + /* Line Wrapping.*/ + enum LineWrappingMode { + LINE_WRAPPING_NONE, + LINE_WRAPPING_BOUNDARY + }; + + /* Gutters. */ + enum GutterType { + GUTTER_TYPE_STRING, + GUTTER_TYPE_ICON, + GUTTER_TYPE_CUSTOM + }; + + /* Contex Menu. */ + enum MenuItems { + MENU_CUT, + MENU_COPY, + MENU_PASTE, + MENU_CLEAR, + MENU_SELECT_ALL, + MENU_UNDO, + MENU_REDO, + MENU_DIR_INHERITED, + MENU_DIR_AUTO, + MENU_DIR_LTR, + MENU_DIR_RTL, + MENU_DISPLAY_UCC, + MENU_INSERT_LRM, + MENU_INSERT_RLM, + MENU_INSERT_LRE, + MENU_INSERT_RLE, + MENU_INSERT_LRO, + MENU_INSERT_RLO, + MENU_INSERT_PDF, + MENU_INSERT_ALM, + MENU_INSERT_LRI, + MENU_INSERT_RLI, + MENU_INSERT_FSI, + MENU_INSERT_PDI, + MENU_INSERT_ZWJ, + MENU_INSERT_ZWNJ, + MENU_INSERT_WJ, + MENU_INSERT_SHY, + MENU_MAX + + }; + + /* Search. */ + enum SearchFlags { + SEARCH_MATCH_CASE = 1, + SEARCH_WHOLE_WORDS = 2, + SEARCH_BACKWARDS = 4 + }; + private: struct GutterInfo { GutterType type = GutterType::GUTTER_TYPE_STRING; @@ -68,11 +123,6 @@ private: ObjectID custom_draw_obj = ObjectID(); StringName custom_draw_callback; }; - Vector<GutterInfo> gutters; - int gutters_width = 0; - int gutter_padding = 0; - - void _update_gutter_width(); class Text { public: @@ -121,7 +171,7 @@ private: void set_font(const Ref<Font> &p_font); void set_font_size(int p_font_size); void set_font_features(const Dictionary &p_features); - void set_direction_and_language(TextServer::Direction p_direction, String p_language); + void set_direction_and_language(TextServer::Direction p_direction, const String &p_language); void set_draw_control_chars(bool p_draw_control_chars); int get_line_height(int p_line, int p_wrap_index) const; @@ -159,7 +209,7 @@ private: void set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { text.write[p_line].gutters.write[p_gutter].text = p_text; } const String &get_line_gutter_text(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].text; } - void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) { text.write[p_line].gutters.write[p_gutter].icon = p_icon; } + void set_line_gutter_icon(int p_line, int p_gutter, const Ref<Texture2D> &p_icon) { text.write[p_line].gutters.write[p_gutter].icon = p_icon; } const Ref<Texture2D> &get_line_gutter_icon(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].icon; } void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { text.write[p_line].gutters.write[p_gutter].color = p_color; } @@ -173,38 +223,48 @@ private: const Color get_line_background_color(int p_line) const { return text[p_line].background_color; } }; - struct Cursor { - Point2 draw_pos; - bool visible = false; - int last_fit_x = 0; - int line = 0; - int column = 0; ///< cursor - int x_ofs = 0; - int line_ofs = 0; - int wrap_ofs = 0; - } cursor; + /* Text */ + Text text; - struct Selection { - SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE; - int selecting_line = 0; - int selecting_column = 0; - int selected_word_beg = 0; - int selected_word_end = 0; - int selected_word_origin = 0; - bool selecting_text = false; + bool setting_text = false; - bool active = false; + // Text properties. + String ime_text = ""; + Point2 ime_selection; - int from_line = 0; - int from_column = 0; - int to_line = 0; - int to_column = 0; + /* Initialise to opposite first, so we get past the early-out in set_editable. */ + bool editable = false; - bool shiftclick_left = false; - } selection; + TextDirection text_direction = TEXT_DIRECTION_AUTO; + TextDirection input_direction = TEXT_DIRECTION_LTR; - Map<int, Dictionary> syntax_highlighting_cache; + Dictionary opentype_features; + String language = ""; + Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + Array st_args; + + void _clear(); + void _update_caches(); + + // User control. + bool overtype_mode = false; + bool context_menu_enabled = true; + bool shortcut_keys_enabled = true; + bool virtual_keyboard_enabled = true; + + // Overridable actions + String cut_copy_line = ""; + + // Context menu. + PopupMenu *menu = nullptr; + PopupMenu *menu_dir = nullptr; + PopupMenu *menu_ctl = nullptr; + + void _generate_context_menu(); + int _get_menu_action_accelerator(const String &p_action); + + /* Versioning */ struct TextOperation { enum Type { TYPE_NONE, @@ -224,248 +284,249 @@ private: bool chain_backward = false; }; - String ime_text; - Point2 ime_selection; + bool undo_enabled = true; + int undo_stack_max_size = 50; - TextOperation current_op; + bool next_operation_is_complex = false; + TextOperation current_op; List<TextOperation> undo_stack; List<TextOperation>::Element *undo_stack_pos = nullptr; - int undo_stack_max_size; - void _clear_redo(); + Timer *idle_detect; + + uint32_t version = 0; + uint32_t saved_version = 0; + + void _push_current_op(); void _do_text_op(const TextOperation &p_op, bool p_reverse); + void _clear_redo(); - //syntax coloring - Ref<SyntaxHighlighter> syntax_highlighter; - Set<String> keywords; + /* Search */ + Color search_result_color = Color(1, 1, 1); + Color search_result_border_color = Color(1, 1, 1); - Dictionary _get_line_syntax_highlighting(int p_line); + String search_text = ""; + uint32_t search_flags = 0; - bool setting_text = false; + int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const; - // data - Text text; + /* Tooltip. */ + Object *tooltip_obj = nullptr; + StringName tooltip_func; + Variant tooltip_ud; - Dictionary opentype_features; - String language; - TextDirection text_direction = TEXT_DIRECTION_AUTO; - TextDirection input_direction = TEXT_DIRECTION_LTR; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; - Array st_args; - bool draw_control_chars = false; + /* Mouse */ + int _get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const; - uint32_t version = 0; - uint32_t saved_version = 0; + /* Caret. */ + struct Caret { + Point2 draw_pos; + bool visible = false; + int last_fit_x = 0; + int line = 0; + int column = 0; + int x_ofs = 0; + int line_ofs = 0; + int wrap_ofs = 0; + } caret; - bool readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly. + bool setting_caret_line = false; + bool caret_pos_dirty = false; + + Color caret_color = Color(1, 1, 1); + Color caret_background_color = Color(0, 0, 0); + + CaretType caret_type = CaretType::CARET_TYPE_LINE; - Timer *caret_blink_timer; - bool caret_blink_enabled = false; bool draw_caret = true; - bool window_has_focus = true; - bool block_caret = false; - bool right_click_moves_caret = true; - bool mid_grapheme_caret_enabled = false; - bool wrap_enabled = false; - int wrap_at = 0; - int wrap_right_offset = 10; + bool caret_blink_enabled = false; + Timer *caret_blink_timer; - bool first_draw = true; - bool setting_row = false; - bool draw_tabs = false; - bool draw_spaces = false; - bool override_selected_font_color = false; - bool cursor_changed_dirty = false; - bool text_changed_dirty = false; - bool undo_enabled = true; - bool hiding_enabled = false; - bool draw_minimap = false; - int minimap_width = 80; - Point2 minimap_char_size = Point2(1, 2); - int minimap_line_spacing = 1; + bool move_caret_on_right_click = true; - bool highlight_all_occurrences = false; - bool scroll_past_end_of_file_enabled = false; - bool highlight_current_line = false; + bool caret_mid_grapheme_enabled = false; - String cut_copy_line; - bool insert_mode = false; - bool select_identifiers_enabled = false; + void _emit_caret_changed(); - bool smooth_scroll_enabled = false; - bool scrolling = false; - bool dragging_selection = false; - bool dragging_minimap = false; - bool can_drag_minimap = false; - bool minimap_clicked = false; - double minimap_scroll_ratio = 0.0; - double minimap_scroll_click_pos = 0.0; - float target_v_scroll = 0.0; - float v_scroll_speed = 80.0; + void _reset_caret_blink_timer(); + void _toggle_draw_caret(); - String lookup_symbol_word; + int _get_column_x_offset_for_line(int p_char, int p_line) const; - uint64_t last_dblclk = 0; - Vector2 last_dblclk_pos; + /* Selection. */ + struct Selection { + SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE; + int selecting_line = 0; + int selecting_column = 0; + int selected_word_beg = 0; + int selected_word_end = 0; + int selected_word_origin = 0; + bool selecting_text = false; + + bool active = false; + + int from_line = 0; + int from_column = 0; + int to_line = 0; + int to_column = 0; + + bool shiftclick_left = false; + } selection; + + bool selecting_enabled = true; + + Color font_selected_color = Color(1, 1, 1); + Color selection_color = Color(1, 1, 1); + bool override_selected_font_color = false; + + bool dragging_selection = false; - Timer *idle_detect; Timer *click_select_held; - HScrollBar *h_scroll; - VScrollBar *v_scroll; - bool updating_scrolls = false; + uint64_t last_dblclk = 0; + Vector2 last_dblclk_pos; + void _click_selection_held(); - Object *tooltip_obj = nullptr; - StringName tooltip_func; - Variant tooltip_ud; + void _update_selection_mode_pointer(); + void _update_selection_mode_word(); + void _update_selection_mode_line(); - bool next_operation_is_complex = false; + void _pre_shift_selection(); + void _post_shift_selection(); - String search_text; - uint32_t search_flags = 0; - int search_result_line = 0; - int search_result_col = 0; + /* line wrapping. */ + LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE; - bool selecting_enabled = true; + int wrap_at_column = 0; + int wrap_right_offset = 10; - bool context_menu_enabled = true; - bool shortcut_keys_enabled = true; - bool virtual_keyboard_enabled = true; + void _update_wrap_at_column(bool p_force = false); - int get_visible_rows() const; - int get_total_visible_rows() const; + void _update_caret_wrap_offset(); - int _get_minimap_visible_rows() const; + /* Viewport. */ + HScrollBar *h_scroll; + VScrollBar *v_scroll; - void update_cursor_wrap_offset(); - void _update_wrap_at(bool p_force = false); - Vector<String> get_wrap_rows_text(int p_line) const; - int get_cursor_wrap_index() const; - int get_char_count(); + bool scroll_past_end_of_file_enabled = false; - double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const; - void set_line_as_first_visible(int p_line, int p_wrap_index = 0); - void set_line_as_center_visible(int p_line, int p_wrap_index = 0); - void set_line_as_last_visible(int p_line, int p_wrap_index = 0); - int get_first_visible_line() const; - int get_last_full_visible_line() const; - int get_last_full_visible_line_wrap_index() const; - double get_visible_rows_offset() const; - double get_v_scroll_offset() const; + // Smooth scrolling. + bool smooth_scroll_enabled = false; + float target_v_scroll = 0.0; + float v_scroll_speed = 80.0; - int get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const; - int get_column_x_offset_for_line(int p_char, int p_line) const; + // Scrolling. + bool scrolling = false; + bool updating_scrolls = false; - void adjust_viewport_to_cursor(); - double get_scroll_line_diff() const; - void _scroll_moved(double); void _update_scrollbars(); + int _get_control_height() const; + void _v_scroll_input(); - void _click_selection_held(); + void _scroll_moved(double p_to_val); - void _update_selection_mode_pointer(); - void _update_selection_mode_word(); - void _update_selection_mode_line(); + double _get_visible_lines_offset() const; + double _get_v_scroll_offset() const; - void _update_minimap_click(); - void _update_minimap_drag(); void _scroll_up(real_t p_delta); void _scroll_down(real_t p_delta); - void _pre_shift_selection(); - void _post_shift_selection(); - void _scroll_lines_up(); void _scroll_lines_down(); - //void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask); - Size2 get_minimum_size() const override; - int _get_control_height() const; + // Minimap + bool draw_minimap = false; - int _get_menu_action_accelerator(const String &p_action); + int minimap_width = 80; + Point2 minimap_char_size = Point2(1, 2); + int minimap_line_spacing = 1; - void _reset_caret_blink_timer(); - void _toggle_draw_caret(); + // minimap scroll + bool minimap_clicked = false; + bool dragging_minimap = false; + bool can_drag_minimap = false; - void _update_caches(); - void _cursor_changed_emit(); - void _text_changed_emit(); + double minimap_scroll_ratio = 0.0; + double minimap_scroll_click_pos = 0.0; - void _push_current_op(); + void _update_minimap_click(); + void _update_minimap_drag(); - /* super internal api, undo/redo builds on it */ + /* Gutters. */ + Vector<GutterInfo> gutters; + int gutters_width = 0; + int gutter_padding = 0; - void _base_insert_text(int p_line, int p_char, const String &p_text, int &r_end_line, int &r_end_column); - String _base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const; - void _base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column); + void _update_gutter_width(); - int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column); + /* Syntax highlighting. */ + Ref<SyntaxHighlighter> syntax_highlighter; + Map<int, Dictionary> syntax_highlighting_cache; - Dictionary _search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const; + Dictionary _get_line_syntax_highlighting(int p_line); - PopupMenu *menu = nullptr; - PopupMenu *menu_dir = nullptr; - PopupMenu *menu_ctl = nullptr; + /* Visual. */ + Ref<StyleBox> style_normal; + Ref<StyleBox> style_focus; + Ref<StyleBox> style_readonly; - void _ensure_menu(); + Ref<Texture2D> tab_icon; + Ref<Texture2D> space_icon; - void _clear(); + Ref<Font> font; + int font_size = 16; + Color font_color = Color(1, 1, 1); + Color font_readonly_color = Color(1, 1, 1); - // Methods used in shortcuts - void _swap_current_input_direction(); - void _new_line(bool p_split_current = true, bool p_above = false); - void _move_cursor_left(bool p_select, bool p_move_by_word = false); - void _move_cursor_right(bool p_select, bool p_move_by_word = false); - void _move_cursor_up(bool p_select); - void _move_cursor_down(bool p_select); - void _move_cursor_to_line_start(bool p_select); - void _move_cursor_to_line_end(bool p_select); - void _move_cursor_page_up(bool p_select); - void _move_cursor_page_down(bool p_select); - void _do_backspace(bool p_word = false, bool p_all_to_left = false); - void _delete(bool p_word = false, bool p_all_to_right = false); - void _move_cursor_document_start(bool p_select); - void _move_cursor_document_end(bool p_select); + int outline_size = 0; + Color outline_color = Color(1, 1, 1); -protected: - bool highlight_matching_braces_enabled = false; + int line_spacing = 1; - struct Cache { - Ref<Texture2D> tab_icon; - Ref<Texture2D> space_icon; - Ref<Texture2D> folded_eol_icon; - Ref<StyleBox> style_normal; - Ref<StyleBox> style_focus; - Ref<StyleBox> style_readonly; - Ref<Font> font; - int font_size = 16; - int outline_size = 0; - Color outline_color; - Color caret_color; - Color caret_background_color; - Color font_color; - Color font_selected_color; - Color font_readonly_color; - Color selection_color; - Color code_folding_color; - Color current_line_color; - Color brace_mismatch_color; - Color word_highlighted_color; - Color search_result_color; - Color search_result_border_color; - Color background_color; - - int line_spacing = 1; - int minimap_width = 0; - } cache; + Color background_color = Color(1, 1, 1); + Color current_line_color = Color(1, 1, 1); + Color word_highlighted_color = Color(1, 1, 1); - virtual String get_tooltip(const Point2 &p_pos) const override; + bool window_has_focus = true; + bool first_draw = true; + + bool highlight_current_line = false; + bool highlight_all_occurrences = false; + bool draw_control_chars = false; + bool draw_tabs = false; + bool draw_spaces = false; + + /*** Super internal Core API. Everything builds on it. ***/ + bool text_changed_dirty = false; + void _text_changed_emit(); void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr); void _remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column); - virtual void _gui_input(const Ref<InputEvent> &p_gui_input); + + void _base_insert_text(int p_line, int p_char, const String &p_text, int &r_end_line, int &r_end_column); + String _base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const; + void _base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column); + + /* Input actions. */ + void _swap_current_input_direction(); + void _new_line(bool p_split_current = true, bool p_above = false); + void _move_caret_left(bool p_select, bool p_move_by_word = false); + void _move_caret_right(bool p_select, bool p_move_by_word = false); + void _move_caret_up(bool p_select); + void _move_caret_down(bool p_select); + void _move_caret_to_line_start(bool p_select); + void _move_caret_to_line_end(bool p_select); + void _move_caret_page_up(bool p_select); + void _move_caret_page_down(bool p_select); + void _do_backspace(bool p_word = false, bool p_all_to_left = false); + void _delete(bool p_word = false, bool p_all_to_right = false); + void _move_caret_document_start(bool p_select); + void _move_caret_document_end(bool p_select); + +protected: void _notification(int p_what); + virtual void _gui_input(const Ref<InputEvent> &p_gui_input); static void _bind_methods(); @@ -473,304 +534,344 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + /* Internal API for CodeEdit, pending public API. */ + // brace matching + bool highlight_matching_braces_enabled = false; + Color brace_mismatch_color; + + // Line hiding. + Color code_folding_color = Color(1, 1, 1); + Ref<Texture2D> folded_eol_icon; + + bool hiding_enabled = false; + + void _set_hiding_enabled(bool p_enabled); + bool _is_hiding_enabled() const; + + void _set_line_as_hidden(int p_line, bool p_hidden); + bool _is_line_hidden(int p_line) const; + + void _unhide_all_lines(); + + // Symbol lookup. + String lookup_symbol_word; void _set_symbol_lookup_word(const String &p_symbol); + /* Text manipulation */ + + // Overridable actions + virtual void _handle_unicode_input(const uint32_t p_unicode); + virtual void _backspace(); + + virtual void _cut(); + virtual void _copy(); + virtual void _paste(); + public: - /* Syntax Highlighting. */ - Ref<SyntaxHighlighter> get_syntax_highlighter(); - void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter); + /* General overrides. */ + virtual Size2 get_minimum_size() const override; + virtual bool is_text_field() const override; + virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; + virtual String get_tooltip(const Point2 &p_pos) const override; + void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata); - /* Gutters. */ - void add_gutter(int p_at = -1); - void remove_gutter(int p_gutter); - int get_gutter_count() const; + /* Text */ + // Text properties. + bool has_ime_text() const; - void set_gutter_name(int p_gutter, const String &p_name); - String get_gutter_name(int p_gutter) const; + void set_editable(const bool p_editable); + bool is_editable() const; - void set_gutter_type(int p_gutter, GutterType p_type); - GutterType get_gutter_type(int p_gutter) const; + void set_text_direction(TextDirection p_text_direction); + TextDirection get_text_direction() const; - void set_gutter_width(int p_gutter, int p_width); - int get_gutter_width(int p_gutter) const; - int get_total_gutter_width() const; + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); - void set_gutter_draw(int p_gutter, bool p_draw); - bool is_gutter_drawn(int p_gutter) const; + void set_language(const String &p_language); + String get_language() const; - void set_gutter_clickable(int p_gutter, bool p_clickable); - bool is_gutter_clickable(int p_gutter) const; + void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); + Control::StructuredTextParser get_structured_text_bidi_override() const; + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; - void set_gutter_overwritable(int p_gutter, bool p_overwritable); - bool is_gutter_overwritable(int p_gutter) const; + void set_tab_size(const int p_size); + int get_tab_size() const; - void merge_gutters(int p_from_line, int p_to_line); + // User controls + void set_overtype_mode_enabled(const bool p_enabled); + bool is_overtype_mode_enabled() const; - void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback); + void set_context_menu_enabled(bool p_enable); + bool is_context_menu_enabled() const; - // Line gutters. - void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata); - Variant get_line_gutter_metadata(int p_line, int p_gutter) const; + void set_shortcut_keys_enabled(bool p_enabled); + bool is_shortcut_keys_enabled() const; - void set_line_gutter_text(int p_line, int p_gutter, const String &p_text); - String get_line_gutter_text(int p_line, int p_gutter) const; + void set_virtual_keyboard_enabled(bool p_enable); + bool is_virtual_keyboard_enabled() const; - void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon); - Ref<Texture2D> get_line_gutter_icon(int p_line, int p_gutter) const; + // Text manipulation + void clear(); - void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color); - Color get_line_gutter_item_color(int p_line, int p_gutter); + void set_text(const String &p_text); + String get_text() const; + int get_line_count() const; - void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable); - bool is_line_gutter_clickable(int p_line, int p_gutter) const; + void set_line(int p_line, const String &p_new_text); + String get_line(int p_line) const; - // Line style - void set_line_background_color(int p_line, const Color &p_color); - Color get_line_background_color(int p_line); + int get_line_width(int p_line, int p_wrap_index = -1) const; + int get_line_height() const; - enum MenuItems { - MENU_CUT, - MENU_COPY, - MENU_PASTE, - MENU_CLEAR, - MENU_SELECT_ALL, - MENU_UNDO, - MENU_REDO, - MENU_DIR_INHERITED, - MENU_DIR_AUTO, - MENU_DIR_LTR, - MENU_DIR_RTL, - MENU_DISPLAY_UCC, - MENU_INSERT_LRM, - MENU_INSERT_RLM, - MENU_INSERT_LRE, - MENU_INSERT_RLE, - MENU_INSERT_LRO, - MENU_INSERT_RLO, - MENU_INSERT_PDF, - MENU_INSERT_ALM, - MENU_INSERT_LRI, - MENU_INSERT_RLI, - MENU_INSERT_FSI, - MENU_INSERT_PDI, - MENU_INSERT_ZWJ, - MENU_INSERT_ZWNJ, - MENU_INSERT_WJ, - MENU_INSERT_SHY, - MENU_MAX + int get_indent_level(int p_line) const; + int get_first_non_whitespace_column(int p_line) const; - }; + void swap_lines(int p_from_line, int p_to_line); - enum SearchFlags { - SEARCH_MATCH_CASE = 1, - SEARCH_WHOLE_WORDS = 2, - SEARCH_BACKWARDS = 4 - }; + void insert_line_at(int p_at, const String &p_text); + void insert_text_at_caret(const String &p_text); - virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; + void remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column); - Point2 _get_local_mouse_pos() const; - void _get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const; - void _get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const; - bool is_dragging_cursor() const; + int get_last_unhidden_line() const; + int get_next_visible_line_offset_from(int p_line_from, int p_visible_amount) const; + Point2i get_next_visible_line_index_offset_from(int p_line_from, int p_wrap_index_from, int p_visible_amount) const; - //void delete_char(); - //void delete_line(); + // Overridable actions + void handle_unicode_input(const uint32_t p_unicode); + void backspace(); + void cut(); + void copy(); + void paste(); + + // Context menu. + PopupMenu *get_menu() const; + bool is_menu_visible() const; + void menu_option(int p_option); + + /* Versioning */ void begin_complex_operation(); void end_complex_operation(); - bool is_insert_text_operation(); - - void set_text_direction(TextDirection p_text_direction); - TextDirection get_text_direction() const; + void undo(); + void redo(); + void clear_undo_history(); - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); + bool is_insert_text_operation() const; - void set_language(const String &p_language); - String get_language() const; + void tag_saved_version(); - void set_draw_control_chars(bool p_draw_control_chars); - bool get_draw_control_chars() const; + uint32_t get_version() const; + uint32_t get_saved_version() const; - void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override() const; + /* Search */ + void set_search_text(const String &p_search_text); + void set_search_flags(uint32_t p_flags); - void set_structured_text_bidi_override_options(Array p_args); - Array get_structured_text_bidi_override_options() const; + Point2i search(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const; - void set_text(String p_text); - void insert_text_at_cursor(const String &p_text); - void insert_at(const String &p_text, int at); - int get_line_count() const; - int get_line_width(int p_line, int p_wrap_offset = -1) const; - int get_line_wrap_index_at_col(int p_line, int p_column) const; - - void set_line_as_hidden(int p_line, bool p_hidden); - bool is_line_hidden(int p_line) const; - void unhide_all_lines(); - int num_lines_from(int p_line_from, int visible_amount) const; - int num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const; - int get_last_unhidden_line() const; + /* Mouse */ + Point2 get_local_mouse_pos() const; - String get_text(); - String get_line(int line) const; - bool has_ime_text() const; - void set_line(int line, String new_text); - int get_row_height() const; + String get_word_at_pos(const Vector2 &p_pos) const; - int get_indent_level(int p_line) const; - int get_first_non_whitespace_column(int p_line) const; + Point2i get_line_column_at_pos(const Point2i &p_pos) const; + int get_minimap_line_at_pos(const Point2i &p_pos) const; - inline void set_scroll_pass_end_of_file(bool p_enabled) { - scroll_past_end_of_file_enabled = p_enabled; - update(); - } + bool is_dragging_cursor() const; - void center_viewport_to_cursor(); + /* Caret */ + void set_caret_type(CaretType p_type); + CaretType get_caret_type() const; - void set_mid_grapheme_caret_enabled(const bool p_enabled); - bool get_mid_grapheme_caret_enabled() const; + void set_caret_blink_enabled(const bool p_enabled); + bool is_caret_blink_enabled() const; - void cursor_set_column(int p_col, bool p_adjust_viewport = true); - void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0); + void set_caret_blink_speed(const float p_speed); + float get_caret_blink_speed() const; - Point2 get_caret_draw_pos() const; - bool is_caret_visible() const; - int cursor_get_column() const; - int cursor_get_line() const; - Vector2i _get_cursor_pixel_pos(bool p_adjust_viewport = true); + void set_move_caret_on_right_click_enabled(const bool p_enable); + bool is_move_caret_on_right_click_enabled() const; - bool cursor_get_blink_enabled() const; - void cursor_set_blink_enabled(const bool p_enabled); + void set_caret_mid_grapheme_enabled(const bool p_enabled); + bool is_caret_mid_grapheme_enabled() const; - float cursor_get_blink_speed() const; - void cursor_set_blink_speed(const float p_speed); + bool is_caret_visible() const; + Point2 get_caret_draw_pos() const; - void cursor_set_block_mode(const bool p_enable); - bool cursor_is_block_mode() const; + void set_caret_line(int p_line, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0); + int get_caret_line() const; - void set_right_click_moves_caret(bool p_enable); - bool is_right_click_moving_caret() const; + void set_caret_column(int p_col, bool p_adjust_viewport = true); + int get_caret_column() const; - SelectionMode get_selection_mode() const; - void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1); - int get_selection_line() const; - int get_selection_column() const; + int get_caret_wrap_index() const; - void set_readonly(bool p_readonly); - bool is_readonly() const; + String get_word_under_caret() const; - void set_wrap_enabled(bool p_wrap_enabled); - bool is_wrap_enabled() const; - bool line_wraps(int line) const; - int times_line_wraps(int line) const; + /* Selection. */ + void set_selecting_enabled(const bool p_enabled); + bool is_selecting_enabled() const; - void clear(); + void set_override_selected_font_color(bool p_override_selected_font_color); + bool is_overriding_selected_font_color() const; - void delete_selection(); + void set_selection_mode(SelectionMode p_mode, int p_line = -1, int p_column = -1); + SelectionMode get_selection_mode() const; - virtual void handle_unicode_input(uint32_t p_unicode); - virtual void backspace(); - void cut(); - void copy(); - void paste(); void select_all(); void select_word_under_caret(); void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column); - void deselect(); - void swap_lines(int line1, int line2); - void set_search_text(const String &p_search_text); - void set_search_flags(uint32_t p_flags); - void set_current_search_result(int line, int col); + bool has_selection() const; + + String get_selected_text() const; + + int get_selection_line() const; + int get_selection_column() const; - void set_highlight_all_occurrences(const bool p_enabled); - bool is_highlight_all_occurrences_enabled() const; - bool is_selection_active() const; int get_selection_from_line() const; int get_selection_from_column() const; int get_selection_to_line() const; int get_selection_to_column() const; - String get_selection_text() const; - String get_word_under_cursor() const; - String get_word_at_pos(const Vector2 &p_pos) const; + void deselect(); + void delete_selection(); - bool search(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column, int &r_line, int &r_column) const; + /* line wrapping. */ + void set_line_wrapping_mode(LineWrappingMode p_wrapping_mode); + LineWrappingMode get_line_wrapping_mode() const; - void undo(); - void redo(); - void clear_undo_history(); + bool is_line_wrapped(int p_line) const; + int get_line_wrap_count(int p_line) const; + int get_line_wrap_index_at_column(int p_line, int p_column) const; - void set_tab_size(const int p_size); - int get_tab_size() const; - void set_draw_tabs(bool p_draw); - bool is_drawing_tabs() const; - void set_draw_spaces(bool p_draw); - bool is_drawing_spaces() const; - void set_override_selected_font_color(bool p_override_selected_font_color); - bool is_overriding_selected_font_color() const; + Vector<String> get_line_wrapped_text(int p_line) const; - void set_insert_mode(bool p_enabled); - bool is_insert_mode() const; + /* Viewport. */ + // Scrolling. + void set_smooth_scroll_enabled(const bool p_enable); + bool is_smooth_scroll_enabled() const; + + void set_scroll_past_end_of_file_enabled(const bool p_enabled); + bool is_scroll_past_end_of_file_enabled() const; - double get_v_scroll() const; void set_v_scroll(double p_scroll); + double get_v_scroll() const; - int get_h_scroll() const; void set_h_scroll(int p_scroll); - - void set_smooth_scroll_enabled(bool p_enable); - bool is_smooth_scroll_enabled() const; + int get_h_scroll() const; void set_v_scroll_speed(float p_speed); float get_v_scroll_speed() const; - uint32_t get_version() const; - uint32_t get_saved_version() const; - void tag_saved_version(); + double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const; - void menu_option(int p_option); + // Visible lines. + void set_line_as_first_visible(int p_line, int p_wrap_index = 0); + int get_first_visible_line() const; - void set_highlight_current_line(bool p_enabled); - bool is_highlight_current_line_enabled() const; + void set_line_as_center_visible(int p_line, int p_wrap_index = 0); + + void set_line_as_last_visible(int p_line, int p_wrap_index = 0); + int get_last_full_visible_line() const; + int get_last_full_visible_line_wrap_index() const; + + int get_visible_line_count() const; + int get_total_visible_line_count() const; + + // Auto Adjust + void adjust_viewport_to_caret(); + void center_viewport_to_caret(); + // Minimap void set_draw_minimap(bool p_draw); bool is_drawing_minimap() const; void set_minimap_width(int p_minimap_width); int get_minimap_width() const; - void set_hiding_enabled(bool p_enabled); - bool is_hiding_enabled() const; + int get_minimap_visible_lines() const; - void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata); + /* Gutters. */ + void add_gutter(int p_at = -1); + void remove_gutter(int p_gutter); + int get_gutter_count() const; - void set_context_menu_enabled(bool p_enable); - bool is_context_menu_enabled(); + void set_gutter_name(int p_gutter, const String &p_name); + String get_gutter_name(int p_gutter) const; - void set_selecting_enabled(bool p_enabled); - bool is_selecting_enabled() const; + void set_gutter_type(int p_gutter, GutterType p_type); + GutterType get_gutter_type(int p_gutter) const; - void set_shortcut_keys_enabled(bool p_enabled); - bool is_shortcut_keys_enabled() const; + void set_gutter_width(int p_gutter, int p_width); + int get_gutter_width(int p_gutter) const; + int get_total_gutter_width() const; - void set_virtual_keyboard_enabled(bool p_enable); - bool is_virtual_keyboard_enabled() const; + void set_gutter_draw(int p_gutter, bool p_draw); + bool is_gutter_drawn(int p_gutter) const; - bool is_menu_visible() const; - PopupMenu *get_menu() const; + void set_gutter_clickable(int p_gutter, bool p_clickable); + bool is_gutter_clickable(int p_gutter) const; + + void set_gutter_overwritable(int p_gutter, bool p_overwritable); + bool is_gutter_overwritable(int p_gutter) const; + + void merge_gutters(int p_from_line, int p_to_line); + + void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback); + + // Line gutters. + void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata); + Variant get_line_gutter_metadata(int p_line, int p_gutter) const; + + void set_line_gutter_text(int p_line, int p_gutter, const String &p_text); + String get_line_gutter_text(int p_line, int p_gutter) const; + + void set_line_gutter_icon(int p_line, int p_gutter, const Ref<Texture2D> &p_icon); + Ref<Texture2D> get_line_gutter_icon(int p_line, int p_gutter) const; + + void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color); + Color get_line_gutter_item_color(int p_line, int p_gutter) const; + + void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable); + bool is_line_gutter_clickable(int p_line, int p_gutter) const; + + // Line style + void set_line_background_color(int p_line, const Color &p_color); + Color get_line_background_color(int p_line) const; + + /* Syntax Highlighting. */ + void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter); + Ref<SyntaxHighlighter> get_syntax_highlighter() const; + + /* Visual. */ + void set_highlight_current_line(bool p_enabled); + bool is_highlight_current_line_enabled() const; + + void set_highlight_all_occurrences(const bool p_enabled); + bool is_highlight_all_occurrences_enabled() const; + + void set_draw_control_chars(bool p_draw_control_chars); + bool get_draw_control_chars() const; + + void set_draw_tabs(bool p_draw); + bool is_drawing_tabs() const; + + void set_draw_spaces(bool p_draw); + bool is_drawing_spaces() const; - virtual bool is_text_field() const override; TextEdit(); - ~TextEdit(); }; -VARIANT_ENUM_CAST(TextEdit::GutterType); +VARIANT_ENUM_CAST(TextEdit::CaretType); +VARIANT_ENUM_CAST(TextEdit::LineWrappingMode); VARIANT_ENUM_CAST(TextEdit::SelectionMode); +VARIANT_ENUM_CAST(TextEdit::GutterType); VARIANT_ENUM_CAST(TextEdit::MenuItems); VARIANT_ENUM_CAST(TextEdit::SearchFlags); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 5ff92c6ff6..04a533bedb 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -994,15 +994,15 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init for (int i = 0; i < 20; i++) { - GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i + 1), ""); } for (int i = 0; i < 32; i++) { - GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i + 1), ""); } bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false); diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 3c3c643367..00cee9269b 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -91,20 +91,22 @@ uint32_t NavigationMesh::get_collision_mask() const { return collision_mask; } -void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void NavigationMesh::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool NavigationMesh::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool NavigationMesh::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void NavigationMesh::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) { @@ -403,8 +405,8 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &NavigationMesh::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &NavigationMesh::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "mask"), &NavigationMesh::set_source_geometry_mode); ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationMesh::get_source_geometry_mode); diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 99b2b6ff58..1cdf7a07ed 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -118,8 +118,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_source_geometry_mode(SourceGeometryMode p_geometry_mode); SourceGeometryMode get_source_geometry_mode() const; diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 3efa16fe0a..c2dedde0be 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -234,8 +234,8 @@ void PhysicsShapeQueryParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &PhysicsShapeQueryParameters2D::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &PhysicsShapeQueryParameters2D::get_margin); - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &PhysicsShapeQueryParameters2D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsShapeQueryParameters2D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &PhysicsShapeQueryParameters2D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &PhysicsShapeQueryParameters2D::get_collision_mask); ClassDB::bind_method(D_METHOD("set_exclude", "exclude"), &PhysicsShapeQueryParameters2D::set_exclude); ClassDB::bind_method(D_METHOD("get_exclude"), &PhysicsShapeQueryParameters2D::get_exclude); @@ -246,7 +246,7 @@ void PhysicsShapeQueryParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &PhysicsShapeQueryParameters2D::set_collide_with_areas); ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &PhysicsShapeQueryParameters2D::is_collide_with_areas_enabled); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude", PROPERTY_HINT_NONE, itos(Variant::RID) + ":"), "set_exclude", "get_exclude"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion"), "set_motion", "get_motion"); @@ -411,9 +411,9 @@ PhysicsDirectSpaceState2D::PhysicsDirectSpaceState2D() { } void PhysicsDirectSpaceState2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &PhysicsDirectSpaceState2D::_intersect_shape, DEFVAL(32)); ClassDB::bind_method(D_METHOD("cast_motion", "shape"), &PhysicsDirectSpaceState2D::_cast_motion); ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &PhysicsDirectSpaceState2D::_collide_shape, DEFVAL(32)); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 4feaf08808..475f6c475d 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -9129,7 +9129,7 @@ RendererStorageRD::RendererStorageRD() { } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; - sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { @@ -9148,7 +9148,7 @@ RendererStorageRD::RendererStorageRD() { } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; - sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 3a7bf8e7f9..47c6317962 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1598,723 +1598,723 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { //constructors - { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BVEC3, TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - - { "mat2", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "mat2", TYPE_MAT2, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC2, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BOOL, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BOOL, TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BOOL, TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC3, TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_INT, TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_INT, TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_INT, TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UINT, TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UINT, TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "mat2", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "mat2", TYPE_MAT2, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, //conversion scalars - { "int", TYPE_INT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "int", TYPE_INT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "int", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "int", TYPE_INT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "int", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, - { "float", TYPE_FLOAT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "float", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "float", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "float", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "float", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, - { "uint", TYPE_UINT, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, true }, - { "uint", TYPE_UINT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, - { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, TAG_GLOBAL, false }, - { "bool", TYPE_BOOL, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "bool", TYPE_BOOL, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "bool", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_BOOL, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_INT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bool", TYPE_BOOL, { TYPE_UINT, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bool", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, //conversion vectors - { "ivec2", TYPE_IVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec2", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "vec2", TYPE_VEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec2", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "vec2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uvec2", TYPE_UVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec2", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bvec2", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec2", TYPE_BVEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "bvec2", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - - { "ivec3", TYPE_IVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec3", TYPE_IVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "ivec3", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - - { "vec3", TYPE_VEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec3", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "vec3", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uvec3", TYPE_UVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec3", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bvec3", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec3", TYPE_BVEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "bvec3", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - - { "ivec4", TYPE_IVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "ivec4", TYPE_IVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "ivec4", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "vec4", TYPE_VEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "vec4", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "vec4", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "uvec4", TYPE_UVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "uvec4", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "bvec4", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "bvec4", TYPE_BVEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec2", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "vec2", TYPE_VEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec2", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "vec2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uvec2", TYPE_UVEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec2", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bvec2", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_IVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec2", TYPE_BVEC2, { TYPE_UVEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bvec2", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "ivec3", TYPE_IVEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec3", TYPE_IVEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "ivec3", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "vec3", TYPE_VEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec3", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "vec3", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uvec3", TYPE_UVEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec3", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bvec3", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_IVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec3", TYPE_BVEC3, { TYPE_UVEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bvec3", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "ivec4", TYPE_IVEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "ivec4", TYPE_IVEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "ivec4", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "vec4", TYPE_VEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "vec4", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "vec4", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + + { "uvec4", TYPE_UVEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "uvec4", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + + { "bvec4", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_IVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "bvec4", TYPE_BVEC4, { TYPE_UVEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, true }, + { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, //conversion between matrixes - { "mat2", TYPE_MAT2, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat2", TYPE_MAT2, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat3", TYPE_MAT3, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mat4", TYPE_MAT4, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, + { "mat2", TYPE_MAT2, { TYPE_MAT3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat2", TYPE_MAT2, { TYPE_MAT4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_MAT2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat3", TYPE_MAT3, { TYPE_MAT4, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_MAT2, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, + { "mat4", TYPE_MAT4, { TYPE_MAT3, TYPE_VOID }, { "" }, TAG_GLOBAL, false }, //builtins - trigonometry - { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "radians", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "radians", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "radians", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "degrees", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "degrees", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "degrees", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "degrees", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "cos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "cos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "cos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "cos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "tan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "tan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "tan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "tan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "asin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "asin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "asin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "asin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "acos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "acos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "acos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "acos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "cosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "cosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "cosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "cosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "tanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "tanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "tanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "tanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "asinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "asinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "asinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "asinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "acosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "acosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "acosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "acosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "atanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "atanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "atanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "atanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + { "radians", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "degrees" }, TAG_GLOBAL, false }, + + { "degrees", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + { "degrees", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "radians" }, TAG_GLOBAL, false }, + + { "sin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "sin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + + { "cos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "cos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + + { "tan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + { "tan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "angle" }, TAG_GLOBAL, false }, + + { "asin", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asin", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "acos", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acos", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "y_over_x" }, TAG_GLOBAL, false }, + { "atan", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + { "atan", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "y", "x" }, TAG_GLOBAL, false }, + + { "sinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "cosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "cosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "tanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "tanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "asinh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "asinh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "acosh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "acosh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "atanh", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "atanh", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, //builtins - exponential - { "pow", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "pow", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "pow", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "pow", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "log", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "exp2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "log2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "inversesqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "pow", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "pow", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "exp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "exp2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "log2", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "inversesqrt", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, //builtins - common - { "abs", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "abs", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "sign", TYPE_INT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "sign", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "floor", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "floor", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "floor", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "floor", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "trunc", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "round", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "roundEven", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "ceil", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "fract", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "mod", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "modf", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "modf", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "modf", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "modf", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "clamp", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "clamp", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "mix", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "step", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC2, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "step", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "smoothstep", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "isnan", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "isnan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "isnan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "isnan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "isinf", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "isinf", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "isinf", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "isinf", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "floatBitsToInt", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToInt", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToInt", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToInt", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "floatBitsToUint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToUint", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToUint", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "floatBitsToUint", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "intBitsToFloat", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "intBitsToFloat", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "intBitsToFloat", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "intBitsToFloat", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "uintBitsToFloat", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, TAG_GLOBAL, true }, - { "uintBitsToFloat", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "uintBitsToFloat", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "uintBitsToFloat", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "abs", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "abs", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "sign", TYPE_INT, { TYPE_INT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC2, { TYPE_IVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "sign", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "floor", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "floor", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "trunc", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "round", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "roundEven", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "ceil", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "fract", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "mod", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + { "mod", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "x", "y" }, TAG_GLOBAL, false }, + + { "modf", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + { "modf", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "i" }, TAG_GLOBAL, true }, + + { "min", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "min", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "min", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "min", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "min", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "max", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "max", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "max", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "max", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "max", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "clamp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC2, { TYPE_VEC2, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC3, { TYPE_VEC3, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_VEC4, { TYPE_VEC4, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + + { "clamp", TYPE_INT, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC2, { TYPE_IVEC2, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC3, { TYPE_IVEC3, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + { "clamp", TYPE_IVEC4, { TYPE_IVEC4, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, false }, + + { "clamp", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC2, { TYPE_UVEC2, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + { "clamp", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "minVal", "maxVal" }, TAG_GLOBAL, true }, + + { "mix", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_BVEC2, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_BVEC3, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC4, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b", "value" }, TAG_GLOBAL, false }, + + { "step", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC2, { TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC3, { TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "step", TYPE_VEC4, { TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, { "edge", "x" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC2, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC2, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC3, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC3, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + { "smoothstep", TYPE_VEC4, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VEC4, TYPE_VOID }, { "edge0", "edge1", "value" }, TAG_GLOBAL, false }, + + { "isnan", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isnan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "isinf", TYPE_BOOL, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "isinf", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + + { "floatBitsToInt", TYPE_INT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToInt", TYPE_IVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + { "floatBitsToUint", TYPE_UINT, { TYPE_FLOAT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC2, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC3, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "floatBitsToUint", TYPE_UVEC4, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + { "intBitsToFloat", TYPE_FLOAT, { TYPE_INT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC2, { TYPE_IVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC3, { TYPE_IVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "intBitsToFloat", TYPE_VEC4, { TYPE_IVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + + { "uintBitsToFloat", TYPE_FLOAT, { TYPE_UINT, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC2, { TYPE_UVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC3, { TYPE_UVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, + { "uintBitsToFloat", TYPE_VEC4, { TYPE_UVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, true }, //builtins - geometric - { "length", TYPE_FLOAT, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "length", TYPE_FLOAT, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "length", TYPE_FLOAT, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "distance", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "distance", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "distance", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "dot", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "dot", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "dot", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "cross", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "normalize", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "normalize", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "normalize", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "reflect", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "refract", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "faceforward", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "faceforward", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "faceforward", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "matrixCompMult", TYPE_MAT2, { TYPE_MAT2, TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "matrixCompMult", TYPE_MAT3, { TYPE_MAT3, TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "matrixCompMult", TYPE_MAT4, { TYPE_MAT4, TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "outerProduct", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "outerProduct", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "outerProduct", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "transpose", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "transpose", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "transpose", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "determinant", TYPE_FLOAT, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "determinant", TYPE_FLOAT, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "determinant", TYPE_FLOAT, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "inverse", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, TAG_GLOBAL, false }, - { "inverse", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, TAG_GLOBAL, false }, - { "inverse", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "greaterThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "lessThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "lessThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "lessThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "lessThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "greaterThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "greaterThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "greaterThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "greaterThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "equal", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "equal", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "equal", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "equal", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "equal", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "equal", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "equal", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "notEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - - { "notEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "length", TYPE_FLOAT, { TYPE_VEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "distance", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "dot", TYPE_FLOAT, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "cross", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + { "normalize", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "v" }, TAG_GLOBAL, false }, + { "reflect", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "I", "N" }, TAG_GLOBAL, false }, + { "refract", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "I", "N", "eta" }, TAG_GLOBAL, false }, + + { "faceforward", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "N", "I", "Nref" }, TAG_GLOBAL, false }, + { "faceforward", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "N", "I", "Nref" }, TAG_GLOBAL, false }, + { "faceforward", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "N", "I", "Nref" }, TAG_GLOBAL, false }, + + { "matrixCompMult", TYPE_MAT2, { TYPE_MAT2, TYPE_MAT2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "matrixCompMult", TYPE_MAT3, { TYPE_MAT3, TYPE_MAT3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "matrixCompMult", TYPE_MAT4, { TYPE_MAT4, TYPE_MAT4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "outerProduct", TYPE_MAT2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "c", "r" }, TAG_GLOBAL, false }, + { "outerProduct", TYPE_MAT3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "c", "r" }, TAG_GLOBAL, false }, + { "outerProduct", TYPE_MAT4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "c", "r" }, TAG_GLOBAL, false }, + + { "transpose", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "transpose", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "transpose", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + + { "determinant", TYPE_FLOAT, { TYPE_MAT2, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "determinant", TYPE_FLOAT, { TYPE_MAT3, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "determinant", TYPE_FLOAT, { TYPE_MAT4, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + + { "inverse", TYPE_MAT2, { TYPE_MAT2, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "inverse", TYPE_MAT3, { TYPE_MAT3, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + { "inverse", TYPE_MAT4, { TYPE_MAT4, TYPE_VOID }, { "m" }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "greaterThan", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThan", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThan", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThan", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThan", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThan", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "lessThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "lessThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "greaterThanEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThanEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "greaterThanEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "equal", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "equal", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "equal", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "equal", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "equal", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + + { "equal", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "equal", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + + { "notEqual", TYPE_BVEC2, { TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "notEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "notEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC2, { TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, + { "notEqual", TYPE_BVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, true }, - { "notEqual", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "notEqual", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC2, { TYPE_BVEC2, TYPE_BVEC2, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC3, { TYPE_BVEC3, TYPE_BVEC3, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, + { "notEqual", TYPE_BVEC4, { TYPE_BVEC4, TYPE_BVEC4, TYPE_VOID }, { "a", "b" }, TAG_GLOBAL, false }, - { "any", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "any", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "any", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "any", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, - { "all", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "all", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "all", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "all", TYPE_BOOL, { TYPE_BVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, - { "not", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "not", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "not", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC2, { TYPE_BVEC2, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC3, { TYPE_BVEC3, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, + { "not", TYPE_BVEC4, { TYPE_BVEC4, TYPE_VOID }, { "x" }, TAG_GLOBAL, false }, //builtins - texture - { "textureSize", TYPE_IVEC2, { TYPE_SAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_ISAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_USAMPLER2D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER3D, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBE, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBEARRAY, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, - { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - - { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - - { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdx", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdy", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, - - { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, true }, - { "fwidth", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, true }, - { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, true }, - { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLER2D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_ISAMPLER2D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_USAMPLER2D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER2DARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER3D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER3D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER3D, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBE, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBEARRAY, TYPE_INT, TYPE_VOID }, { "sampler", "lod" }, TAG_GLOBAL, true }, + + { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, false }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, false }, + + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "bias" }, TAG_GLOBAL, true }, + + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, false }, + + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + { "textureProjLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID }, { "sampler", "coords", "lod" }, TAG_GLOBAL, true }, + + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + + { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdx", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + { "dFdy", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "dFdy", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + + { "fwidth", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, + { "fwidth", TYPE_VEC4, { TYPE_VEC4, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, //sub-functions //array - { "length", TYPE_INT, { TYPE_VOID }, TAG_ARRAY, true }, + { "length", TYPE_INT, { TYPE_VOID }, { "" }, TAG_ARRAY, true }, // modern functions - { "fma", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, TAG_GLOBAL, false }, - { "fma", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, TAG_GLOBAL, false }, - { "fma", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, TAG_GLOBAL, false }, - { "fma", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, TAG_GLOBAL, false }, + { "fma", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + { "fma", TYPE_VEC2, { TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + { "fma", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, + { "fma", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID }, { "a", "b", "c" }, TAG_GLOBAL, false }, - { nullptr, TYPE_VOID, { TYPE_VOID }, TAG_GLOBAL, false } + { nullptr, TYPE_VOID, { TYPE_VOID }, { "" }, TAG_GLOBAL, false } }; const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = { @@ -8474,6 +8474,12 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += get_datatype_name(builtin_func_defs[idx].args[i]); + String arg_name = (String)builtin_func_defs[idx].args_names[i]; + if (!arg_name.is_empty()) { + calltip += " "; + calltip += arg_name; + } + if (i == completion_argument) { calltip += char32_t(0xFFFF); } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index c02d6c47ec..fae7fa54aa 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -939,6 +939,7 @@ private: const char *name; DataType rettype; const DataType args[MAX_ARGS]; + const char *args_names[MAX_ARGS]; SubClassTag tag; bool high_end; }; |