Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 2.1.1 #70

Merged
merged 82 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
e716490
Added variation of add_rounded_corners in RegularGeometry2D that can …
9thAzure Apr 24, 2024
6405174
Add unit test for RegularGeometry.add_rounded_corners
9thAzure Apr 25, 2024
326d507
fixed bugs found in unit testing
9thAzure Apr 26, 2024
6bf0049
Added documentation
9thAzure Apr 26, 2024
9770d2d
Added functionality to not limit the ending slopes, and added/updated…
9thAzure Apr 26, 2024
d2db3f5
Merge pull request #53 from 9thAzure/add_regular_geometry
9thAzure Apr 26, 2024
5f85e49
changed add_rounded_corners to use RegularGeometry variant.
9thAzure May 3, 2024
8fbebfe
Made the shape and static function work with rings, via optional para…
9thAzure May 3, 2024
5f4cad2
Applied change to all nodes
9thAzure May 4, 2024
c9924a2
Merge pull request #54 from 9thAzure/fix_inconsistant_width
9thAzure May 4, 2024
1909cfe
Add rotate_shape function
9thAzure Apr 14, 2024
40f582d
Added 'apply_size_scale' method
9thAzure Apr 15, 2024
e123214
Added functions to all classes
9thAzure Apr 18, 2024
338d952
added unit tests for methods in regular_polygon_2d (methods work impr…
9thAzure Apr 20, 2024
0bb7f8c
Added helper method to scale shape properly (currently only expansion…
9thAzure Apr 21, 2024
e29e075
temp commit. Bug with improper width on fully drawn arc shapes is mes…
9thAzure Apr 22, 2024
b76d32f
fixed rescaling of ring shapes
9thAzure May 5, 2024
b709307
Unified transformations of scale and rotation for both instance and s…
9thAzure May 5, 2024
51c3a3c
refined unit tests
9thAzure May 5, 2024
364a459
Fixed bugs
9thAzure May 5, 2024
be00ce2
Added ability for widths and corner_size to not be 'fixed,' effective…
9thAzure May 8, 2024
e8fefde
fixed issue with the correction for full arc ringed shape
9thAzure May 9, 2024
ddbe72d
altered order of parameters
9thAzure May 9, 2024
e4f99e8
Added unit tests, and fixed bugs found in unit tests.
9thAzure May 10, 2024
57b9a6c
Added apply_transform to StarPolygon2D
9thAzure May 10, 2024
acc91e3
added apply_transform to SimplePolygon2D
9thAzure May 10, 2024
c83408e
Added method to regular_collision_polygon
9thAzure May 18, 2024
7f8b357
Added documentation comments
9thAzure May 19, 2024
bd6c9c2
removed old methods in collision polygon
9thAzure May 19, 2024
dd28161
changes to apply_transform to make it more efficient
9thAzure May 19, 2024
166f5d2
changed default of scale width/corner size to true
9thAzure May 19, 2024
cf1aa34
modified documentation
9thAzure May 19, 2024
1ab9e6c
Added c# interop and tests
9thAzure May 22, 2024
98d11cb
unified naming
9thAzure May 22, 2024
ad8cefb
Merge pull request #55 from 9thAzure/add_shape_editing
9thAzure May 22, 2024
6ee97a9
Made class to hold assert_almost_eq_deep and made all unit tests inhe…
9thAzure May 31, 2024
9948688
overhauled some of methods to use assert_almost_eq_deep
9thAzure May 31, 2024
48792df
finished refining unit tests
9thAzure Jun 1, 2024
732be8d
Merge pull request #59 from 9thAzure/improve_unit_tests
9thAzure Jun 2, 2024
41f72b6
addition of methods (that just call queue_redraw) for SimplePolygon2D
9thAzure Jun 2, 2024
8d00944
for StarPolygon2D: point_count > vertices_count, regenerate_polygon -…
9thAzure Jun 3, 2024
85905fc
C# interop for SimplePolygon2D
9thAzure Jun 3, 2024
302d0d2
Similar changes as StarPolygon to RegularPolygon2D
9thAzure Jun 3, 2024
27eaacd
fixed StarPolygon2DTests.cs
9thAzure Jun 3, 2024
f3901c4
additional test fixes
9thAzure Jun 3, 2024
90cd801
deprecated quadratic_bezier_interpolate
9thAzure Jun 3, 2024
c5b50e9
Merge pull request #60 from 9thAzure/replacement_names
9thAzure Jun 3, 2024
e5cdb5c
forgot offset_position in SimplePolygon2D
9thAzure Jun 3, 2024
2f1b15a
Merge branch 'dev' into replacement_names
9thAzure Jun 3, 2024
91676a4
Merge pull request #61 from 9thAzure/replacement_names
9thAzure Jun 3, 2024
cf451c3
minor doc corrections
9thAzure Jun 3, 2024
430b59d
Merge pull request #62 from 9thAzure/doc_corrections
9thAzure Jun 3, 2024
c7d05e2
Added base gui editor
9thAzure May 31, 2024
2196d5b
renamig
9thAzure Jun 3, 2024
d397810
base_handler only works when selection_mode is used (arrow button)
9thAzure Jun 7, 2024
527af9b
Added size_rotation_handler, implemented in simple_polygon2D already …
9thAzure Jun 7, 2024
bbb2e7d
added handler to regular_polygon_2d
9thAzure Jun 8, 2024
9481202
hopefully fixed incorrect button access
9thAzure Jun 8, 2024
c4beae4
reworked handlers to use EditorPlugin._handle().
9thAzure Jun 13, 2024
c1f7a02
proper positioning for collision polygon with star shapes
9thAzure Jun 13, 2024
977ba3a
implemented undo/redo support
9thAzure Jun 13, 2024
4707428
changed handle to be more unique (particularly behind Polygon2D's han…
9thAzure Jun 13, 2024
711fa0e
fixed clamping issue with straight lines, and added clamping in compa…
9thAzure Jun 13, 2024
b7defd8
some spring cleaning
9thAzure Jun 15, 2024
1e5c9d2
some optimizations in reducing '_from_parent' calls
9thAzure Jun 15, 2024
f7ab279
clamped offset_rotation values to postive values
9thAzure Jun 16, 2024
545b507
Fixed handler not reseting the origin of the shape with RegularCollis…
9thAzure Jun 18, 2024
824c810
Updated handler mouse press dectection range to be a circle matching …
9thAzure Jun 18, 2024
bc7302a
Merge pull request #66 from 9thAzure/gui_editor
9thAzure Jun 18, 2024
f934e5b
fixed offset rotation of circles generated by get_shape_vertices
9thAzure Jun 19, 2024
0bf2249
Merge pull request #67 from 9thAzure/fix_circle
9thAzure Jun 19, 2024
d60b06c
Added copy of the license in root level repository
9thAzure Jun 20, 2024
a6e6735
Merge pull request #68 from 9thAzure/add_root_license
9thAzure Jun 20, 2024
1518fb0
improved layering of handler, it should now appear in front of everyt…
9thAzure Jun 20, 2024
c2a3ec3
Made handler only respond when the select mode button is currently se…
9thAzure Jun 20, 2024
41dcea3
Fixed handlers not rescaling with property changes, and renamed 'main…
9thAzure Jun 20, 2024
f4be555
Fixed handler not rescaling when changing parents
9thAzure Jun 20, 2024
7774ce3
minor fix to error messaging of _get_select_mode_button
9thAzure Jun 24, 2024
b696bff
Added additional checks to ensure that button is valid
9thAzure Jun 24, 2024
4387c98
Merge pull request #69 from 9thAzure/more_gui_improvements
9thAzure Jun 24, 2024
f030c2a
updated license badge to now read from repository
9thAzure Jun 24, 2024
9e104d7
Merge pull request #71 from 9thAzure/update_license_badge
9thAzure Jun 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions License.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is a copy of the [license](addons/complex_shape_creation/LICENSE.txt) found in the addon's folder.
```
MIT License

Copyright (c) 2024 9thAzure (GitHub username)

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.
```
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
![Regular Polygon 2D icon](/addons/complex_shape_creation/regular_polygon_2d/regular_polygon_2d.svg)

![GitHub Release](https://img.shields.io/github/v/release/9thAzure/Complex_Shape_Creation)
![Static Badge (License, not detected by github because it is in sub folder)](https://img.shields.io/badge/License-MIT-orange)
![GitHub License](https://img.shields.io/github/license/9thAzure/Complex_Shape_Creation)
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/9thAzure/Complex_Shape_Creation)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/9thAzure/Complex_Shape_Creation/total)

Expand Down Expand Up @@ -81,7 +81,8 @@ All other functions **modify** the array passed in as the argument.
## License

This addon is available under the [MIT](https://mit-license.org/) license,
which is in the addon's [folder](/addons/complex_shape_creation/LICENSE.txt)
which is in the addon's [folder](/addons/complex_shape_creation/LICENSE.txt).
A copy is available in the root [folder](/License.md).

## Plugins / Packages Used

Expand Down
2 changes: 2 additions & 0 deletions addons/complex_shape_creation/MethodName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace ComplexShapeCreation.MemberNames;
public static class MethodName
{
public static readonly StringName GetShapeVertices = new("get_shape_vertices");
[Obsolete("Method name has changed, use 'Regenerate' instead.", false)]
public static readonly StringName RegeneratePolygon = new("regenerate_polygon");
public static readonly StringName UsesPolygonMember = new("uses_polygon_member");
public static readonly StringName GetSideLength = new("get_side_length");
Expand All @@ -20,4 +21,5 @@ public static class MethodName
public static readonly StringName GetStarVertices = new("get_star_vertices");
public static readonly StringName WidenPolyline = new("_widen_polyline_result");
public static readonly StringName WidenMultiline = new("_widen_multiline_result");
public static readonly StringName ApplyTransformation = new("apply_transformation");
}
5 changes: 4 additions & 1 deletion addons/complex_shape_creation/PropertyName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ public static class PropertyName
public static readonly StringName OffsetRotationDegrees = new("offset_rotation_degrees");
public static readonly StringName OffsetRotation = new("offset_rotation");
public static readonly StringName Color = new("color");
[Obsolete("Property name has been replaced, use 'Offset' instead.", false)]
public static readonly StringName OffsetPosition = new("offset_position");
public static readonly StringName Offset = new("offset");
public static readonly StringName Width = new("width");
public static readonly StringName DrawnArcDegrees = new("drawn_arc_degrees");
public static readonly StringName DrawnArc = new("drawn_arc");
public static readonly StringName CornerSize = new("corner_size");
public static readonly StringName CornerSmoothness = new("corner_smoothness");
public static readonly StringName PointCount = new("point_count");
[Obsolete("Property name has been replaced, use 'VerticesCount' instead.", false)]
public static readonly StringName PointCount = new("vertices_count");
public static readonly StringName InnerSize = new("inner_size");
}
137 changes: 137 additions & 0 deletions addons/complex_shape_creation/gui_handlers/base_handler.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
@tool
extends Node2D

var _shift_clamps : Array[Callable] = [clamp_straight_line, clamp_circle_radius, clamp_compass_lines]

var _plugin : EditorPlugin
var _undo_redo_manager : EditorUndoRedoManager
var _parent : Node2D = null
var _origin := Vector2.ZERO
var size := 1.0

var _being_dragged := false
var _old_position := Vector2.ZERO


func _init(plugin : EditorPlugin, undo_redo_manager : EditorUndoRedoManager, handler_size := 9.0) -> void:
_plugin = plugin
_undo_redo_manager = undo_redo_manager
_undo_redo_manager.version_changed.connect(maintain_position)
_undo_redo_manager.version_changed.connect(maintain_editor_scale)
_undo_redo_manager.history_changed.connect(maintain_position)
_undo_redo_manager.history_changed.connect(maintain_editor_scale)
size = handler_size
z_as_relative = false
z_index = RenderingServer.CANVAS_ITEM_Z_MAX

func _ready() -> void:
assert(Engine.is_editor_hint())

_parent = get_parent()

maintain_editor_scale()
maintain_position()

func mouse_press(point : Vector2) -> bool:
const extra_margin := 2.0
if (point - global_position).length_squared() <= ((size + extra_margin) / get_viewport_transform().get_scale().x) ** 2:
_old_position = position
_being_dragged = true
if _old_position == Vector2.ZERO:
_old_position = Vector2.RIGHT
modulate = Color.LIME_GREEN
return true
return false

var suppress_from_parent_call := false
func mouse_release() -> bool:
if _being_dragged:
_being_dragged = false
suppress_from_parent_call = true
_mouse_released()
modulate = Color.WHITE
return true
return false

func _from_parent_properties() -> void:
printerr("'_from_parent_properties' is abstract")

func _update_properties() -> void:
printerr("'_update_properties' is abstract")

func _mouse_released() -> void:
printerr("'_mouse_released' is abstract")

func _draw() -> void:
const margin := 1

var shape := RegularPolygon2D.get_shape_vertices(5, size)
draw_colored_polygon(shape, Color.WHITE)
draw_polyline(shape, Color.BLACK, margin, true)
draw_line(shape[-1], shape[0], Color.BLACK, margin, true)

var previous_editor_scale := 1.0
func _process(_delta) -> void:
var editor_scale := get_viewport_transform().get_scale().x
if not is_equal_approx(editor_scale, previous_editor_scale):
maintain_editor_scale()
previous_editor_scale = editor_scale

if not _being_dragged:
return

global_position = get_global_mouse_position()
if Input.is_key_pressed(KEY_SHIFT):
_clamp_position()
_update_properties()

func maintain_position() -> void:
if suppress_from_parent_call:
suppress_from_parent_call = false
return

_origin = Vector2.ZERO
if not _parent is CollisionShape2D:
_origin = _parent.offset
_from_parent_properties()

func maintain_editor_scale() -> void:
global_transform = Transform2D(0, Vector2.ONE / get_viewport_transform().get_scale().x, 0, global_position)

func _clamp_position() -> void:
if _shift_clamps.size() == 0:
return

var best_position := position
var best_distance := INF
for i in _shift_clamps.size():
var point = _shift_clamps[i].call()
if typeof(point) != TYPE_VECTOR2:
printerr("method %s did not returned a %s, not a vector2." % [_shift_clamps[i], typeof(point)])
continue

var distance : float = (point - position).length_squared()
if distance < best_distance:
best_distance = distance
best_position = point

position = best_position

func clamp_straight_line() -> Vector2:
var allowed_line := _old_position - _origin
var inverse_line := Vector2(-allowed_line.y, allowed_line.x)
var a := RegularGeometry2D._find_intersection(position, inverse_line, _origin, allowed_line)
return position + inverse_line * a

func clamp_circle_radius() -> Vector2:
var radius := (_old_position - _origin).length()
return _origin + (position - _origin).normalized() * radius

func clamp_compass_lines() -> Vector2:
var functional_position := position - _origin
var angle := atan2(functional_position.y, functional_position.x)
var multiplier := floor((angle + TAU / 16) / (TAU / 8))

angle = multiplier * TAU / 8
var slope := Vector2(cos(angle), sin(angle))
return Geometry2D.get_closest_point_to_segment_uncapped(position, _origin, _origin + slope)
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
@tool
extends "res://addons/complex_shape_creation/gui_handlers/base_handler.gd"

var shape_type : int = 0

const _SHAPE_SIMPLE := 1
const _SHAPE_REGULAR := 2
const _SHAPE_STAR := 3
const _SHAPE_COLLISION := 4

func _ready() -> void:
shape_type = _get_shape_type(get_parent())
super._ready()

func _get_shape_type(shape : Node2D) -> int:
if shape is SimplePolygon2D:
return _SHAPE_SIMPLE
if shape is RegularPolygon2D:
return _SHAPE_REGULAR
if shape is StarPolygon2D:
return _SHAPE_STAR
if shape is RegularCollisionPolygon2D:
return _SHAPE_COLLISION

printerr("unrecognized shape given: %s" % shape)
return 0

func _from_parent_properties() -> void:
var offset_rotation : float = _parent.offset_rotation + get_rotation_offset()

position = _origin + Vector2(sin(offset_rotation), -cos(offset_rotation)) * _parent.size

func _update_properties() -> void:
var functional_position := position - _origin
_parent.size = functional_position.length()
_parent.offset_rotation = fmod(atan2(functional_position.y, functional_position.x) + PI / 2 - get_rotation_offset() + TAU, TAU)

func _mouse_released() -> void:
_undo_redo_manager.create_action("Resizing and Rotating Shape")

_undo_redo_manager.add_do_property(_parent, &"size", _parent.size)
_undo_redo_manager.add_do_property(_parent, &"offset_rotation", _parent.offset_rotation)

var old_functional_position := (_old_position - _origin)
var old_size = old_functional_position.length()
var old_rotation = fmod(atan2(old_functional_position.y, old_functional_position.x) + PI / 2 - get_rotation_offset() + TAU, TAU)
_undo_redo_manager.add_undo_property(_parent, &"size", old_size)
_undo_redo_manager.add_undo_property(_parent, &"offset_rotation", old_rotation)

_undo_redo_manager.commit_action()

func get_rotation_offset() -> float:
var offset := 0.0
if not is_parent_star_shape():
offset += PI
if _parent.vertices_count != 2:
offset += TAU / _parent.vertices_count / 2
return offset

func is_parent_star_shape() -> bool:
return shape_type == _SHAPE_STAR or shape_type == _SHAPE_COLLISION and _parent.inner_size > 0
96 changes: 96 additions & 0 deletions addons/complex_shape_creation/plugin.gd
Original file line number Diff line number Diff line change
@@ -1,2 +1,98 @@
@tool
extends EditorPlugin

func _handles(object : Object) -> bool:
return _is_handled_node(object)

func _is_handled_node(object : Object) -> bool:
return (
object is SimplePolygon2D or
object is RegularPolygon2D or
object is RegularCollisionPolygon2D or
object is StarPolygon2D
)

func _edit(object : Object) -> void:
var parent := _size_rotation_handler.get_parent()
if object == null:
if parent != null:
parent.remove_child(_size_rotation_handler)
return

if not is_same(object, parent):
if parent != null:
parent.remove_child(_size_rotation_handler)
_size_rotation_handler.request_ready()
object.add_child(_size_rotation_handler, false, INTERNAL_MODE_BACK)

const BaseHandler := preload("res://addons/complex_shape_creation/gui_handlers/base_handler.gd")
const SizeRotationHandler := preload("res://addons/complex_shape_creation/gui_handlers/size_rotation_handler.gd")
var _size_rotation_handler : SizeRotationHandler
func _make_visible(visible) -> void:
if visible:
_size_rotation_handler = SizeRotationHandler.new(self, get_undo_redo())
else:
_remove(_size_rotation_handler)

func _remove(node : Node) -> void:
var parent := node.get_parent()
if parent != null:
parent.remove_child(node)
node.queue_free()

func _forward_canvas_gui_input(event) -> bool:
if event is InputEventMouseButton:
if event.button_index != MOUSE_BUTTON_MASK_LEFT:
return false

if event.pressed:
if not _select_mode_button_selected():
return false

var viewport := EditorInterface.get_editor_viewport_2d()
var transform := viewport.get_final_transform()
var size := viewport.size
var lower_bound := -transform.get_origin()
lower_bound = Vector2(lower_bound.x / transform.get_scale().x, lower_bound.y / transform.get_scale().y)
var upper_bound := lower_bound + Vector2(size.x / transform.get_scale().x, size.y / transform.get_scale().y)

var mouse_position = viewport.get_mouse_position()
if (lower_bound.x <= mouse_position.x and mouse_position.x <= upper_bound.x and
lower_bound.y <= mouse_position.y and mouse_position.y <= upper_bound.y):
return _size_rotation_handler.mouse_press(mouse_position)
else:
return _size_rotation_handler.mouse_release()

return false

var _select_mode_button : Button = null
func _select_mode_button_selected() -> bool:
if _is_select_mode_button_invalid(_select_mode_button):
_get_select_mode_button()
if _is_select_mode_button_invalid(_select_mode_button):
return true

return _select_mode_button.button_pressed

func _is_select_mode_button_invalid(button : Button) -> bool:
return button == null or not is_instance_valid(button) or not button.toggle_mode or button.icon == null

func _get_select_mode_button() -> void:
var main_screen := EditorInterface.get_editor_main_screen()

var found_node : Node = main_screen.get_node_or_null("@CanvasItemEditor@9465/@MarginContainer@9280/@HFlowContainer@9281/@HBoxContainer@9282/@Button@9329")
if found_node != null and found_node is Button:
_select_mode_button = found_node
return

found_node = main_screen
for i in 5:
if found_node == null:
break
found_node = found_node.get_child(0)

if found_node != null and found_node is Button:
_select_mode_button = found_node
return

printerr("cannot find select button")
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,20 @@ public Vector2 Scale
get => Instance.Scale;
set => Instance.Scale = value;
}

/// <inheritdoc cref="RegularPolygon2D.ApplyTransformation(float, float, bool, bool)"/>
/// <summary>
/// Transforms <see cref="CollisionShape2D.Shape"/>, rotating it by <paramref name="rotation"/> radians and scaling it by a factor of <paramref name="scale"/>.
/// </summary>
/// <remarks>
/// This method modifies the existing <see cref="CollisionShape2D.Shape"/>, so is generally faster than changing <see cref="Size"/> and <see cref="OffsetRotation"/>.
/// This only happens if the transformed shape is congruent to the original. If it is not or <see cref="CollisionShape2D.Shape"/> isn't used, the shape is regenerated.
/// <br/><br/><b>Warning</b>: Currently method does not check if the <see cref="CornerSize"/> value is clamped due to small side lengths.
/// If this occurs in the original or transformed shape and <paramref name="scale_corner_size"/> is <see langword="false"/>, the shape will not be accurate to this node's properties.
/// </remarks>
/// <param name="scale_width">Toggles scaling <see cref="Width"/>, applying correction if <see langword="false"/>.</param>
/// <param name="scale_corner_size">Toggles scaling <see cref="CornerSize"/>, applying correction if <see langword="false"/>.</param>
public void ApplyTransformation(float rotation, float scale, bool scale_width = true, bool scale_corner_size = true) => Instance.Call(MethodName.ApplyTransformation, rotation, scale, scale_width, scale_corner_size);

/// <summary>Creates and wraps a <see cref="CollisionShape2D"/> around <paramref name="instance"/>.</summary>
/// <param name="instance">The instance of <see cref="GDScriptEquivalent"/> to wrap.</param>
Expand Down
Loading