diff --git a/Translations/Translations.pot b/Translations/Translations.pot index b18c5fb3b..9a757a7fd 100644 --- a/Translations/Translations.pot +++ b/Translations/Translations.pot @@ -2158,3 +2158,41 @@ msgstr "" msgid "Reference Images" msgstr "" + +msgid "Crop" +msgstr "" + +msgid "Resize the canvas" +msgstr "" + +msgid "Margins" +msgstr "" + +msgid "Position + Size" +msgstr "" + +msgid "Locked Aspect Ratio" +msgstr "" + +msgid "Margins:" +msgstr "" + +msgid "Aspect Ratio:" +msgstr "" + +msgid "Top:" +msgstr "" + +msgid "Bottom:" +msgstr "" + +msgid "Left:" +msgstr "" + +msgid "Right:" +msgstr "" + +msgid "Locked size\n\n" +"When enabled using the tool on the canvas will only move the cropping rectangle.\n\n" +"When disabled using the tool on the canvas will draw the rectangle." +msgstr "" \ No newline at end of file diff --git a/assets/graphics/misc/locked_size.png b/assets/graphics/misc/locked_size.png new file mode 100644 index 000000000..cc0ff7279 Binary files /dev/null and b/assets/graphics/misc/locked_size.png differ diff --git a/assets/graphics/misc/locked_size.png.import b/assets/graphics/misc/locked_size.png.import new file mode 100644 index 000000000..bcfe69e17 --- /dev/null +++ b/assets/graphics/misc/locked_size.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/locked_size.png-05ec97950456a9ee42557d3414337309.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/misc/locked_size.png" +dest_files=[ "res://.import/locked_size.png-05ec97950456a9ee42557d3414337309.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/assets/graphics/misc/unlocked_size.png b/assets/graphics/misc/unlocked_size.png new file mode 100644 index 000000000..5aee902d9 Binary files /dev/null and b/assets/graphics/misc/unlocked_size.png differ diff --git a/assets/graphics/misc/unlocked_size.png.import b/assets/graphics/misc/unlocked_size.png.import new file mode 100644 index 000000000..bbca431f9 --- /dev/null +++ b/assets/graphics/misc/unlocked_size.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/unlocked_size.png-bd224f1987da9100f208bda51349ff67.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/misc/unlocked_size.png" +dest_files=[ "res://.import/unlocked_size.png-bd224f1987da9100f208bda51349ff67.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/assets/graphics/tools/crop.png b/assets/graphics/tools/crop.png new file mode 100644 index 000000000..b6ea40772 Binary files /dev/null and b/assets/graphics/tools/crop.png differ diff --git a/assets/graphics/tools/crop.png.import b/assets/graphics/tools/crop.png.import new file mode 100644 index 000000000..8d8f188f1 --- /dev/null +++ b/assets/graphics/tools/crop.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/crop.png-b117b864c2b96dd1998350bff3c06cf8.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/tools/crop.png" +dest_files=[ "res://.import/crop.png-b117b864c2b96dd1998350bff3c06cf8.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/assets/graphics/tools/cursors/crop.png b/assets/graphics/tools/cursors/crop.png new file mode 100644 index 000000000..6dcb0445a Binary files /dev/null and b/assets/graphics/tools/cursors/crop.png differ diff --git a/assets/graphics/tools/cursors/crop.png.import b/assets/graphics/tools/cursors/crop.png.import new file mode 100644 index 000000000..bb2158565 --- /dev/null +++ b/assets/graphics/tools/cursors/crop.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/crop.png-b1dd939167138aaa21a7eeec94aed6bc.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/tools/cursors/crop.png" +dest_files=[ "res://.import/crop.png-b1dd939167138aaa21a7eeec94aed6bc.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=false +svg/scale=1.0 diff --git a/project.godot b/project.godot index 393eccd00..e796bf041 100644 --- a/project.godot +++ b/project.godot @@ -79,6 +79,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://src/UI/Nodes/CollapsibleContainer.gd" }, { +"base": "Node2D", +"class": "CropRect", +"language": "GDScript", +"path": "res://src/UI/Canvas/CropRect.gd" +}, { "base": "Reference", "class": "Drawer", "language": "GDScript", @@ -244,6 +249,7 @@ _global_script_class_icons={ "Brushes": "", "Canvas": "", "CollapsibleContainer": "", +"CropRect": "", "Drawer": "", "Frame": "", "GIFAnimationExporter": "", diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd index fc1e655b4..5d13e999f 100644 --- a/src/Autoload/Global.gd +++ b/src/Autoload/Global.gd @@ -152,6 +152,12 @@ var onion_skinning_blue_red := false # Palettes var palettes := {} +# Crop Options: +var crop_top := 0 +var crop_bottom := 0 +var crop_left := 0 +var crop_right := 0 + # Nodes var pixel_layer_button_node: PackedScene = preload("res://src/UI/Timeline/PixelLayerButton.tscn") var group_layer_button_node: PackedScene = preload("res://src/UI/Timeline/GroupLayerButton.tscn") diff --git a/src/Autoload/Tools.gd b/src/Autoload/Tools.gd index cefe722bf..64e36e209 100644 --- a/src/Autoload/Tools.gd +++ b/src/Autoload/Tools.gd @@ -86,6 +86,8 @@ var tools := { preload("res://src/Tools/ColorPicker.tscn"), "Select a color from a pixel of the sprite" ), + "Crop": + Tool.new("Crop", "Crop", "crop", preload("res://src/Tools/CropTool.tscn"), "Resize the canvas"), "Pencil": Tool.new( "Pencil", diff --git a/src/Classes/Project.gd b/src/Classes/Project.gd index 403c56f3a..1a9aeaab8 100644 --- a/src/Classes/Project.gd +++ b/src/Classes/Project.gd @@ -474,6 +474,7 @@ func _size_changed(value: Vector2) -> void: tiles.tile_size = value tiles.reset_mask() size = value + Global.canvas.crop_rect.reset() func change_cel(new_frame: int, new_layer := -1) -> void: diff --git a/src/Tools/CropTool.gd b/src/Tools/CropTool.gd new file mode 100644 index 000000000..758fbd3d7 --- /dev/null +++ b/src/Tools/CropTool.gd @@ -0,0 +1,210 @@ +extends BaseTool +# Crop Tool, allows you to resize the canvas interactively + +var _offset = Vector2.ZERO +var _crop: CropRect +var _start_pos: Vector2 +var _syncing := false + + +func _ready() -> void: + _crop = Global.canvas.crop_rect + _crop.connect("updated", self, "_sync_ui") + _crop.tool_count += 1 + _sync_ui() + + +func _exit_tree() -> void: + _crop.tool_count -= 1 + + +func draw_start(position: Vector2) -> void: + .draw_start(position) + _offset = position - _crop.rect.position + _start_pos = position + + +func draw_move(position: Vector2) -> void: + .draw_move(position) + if _crop.locked_size: + _crop.rect.position = position - _offset + else: + match _crop.mode: + CropRect.Mode.MARGINS, CropRect.Mode.POSITION_SIZE: + _crop.rect.position.x = min(_start_pos.x, position.x) + _crop.rect.position.y = min(_start_pos.y, position.y) + _crop.rect.end.x = max(_start_pos.x, position.x) + _crop.rect.end.y = max(_start_pos.y, position.y) + CropRect.Mode.LOCKED_ASPECT_RATIO: + var distance = abs(_start_pos.x - position.x) + abs(_start_pos.y - position.y) + _crop.rect.size.x = round( + distance * _crop.ratio.x / (_crop.ratio.x + _crop.ratio.y) + ) + _crop.rect.size.y = round( + distance * _crop.ratio.y / (_crop.ratio.x + _crop.ratio.y) + ) + if _start_pos.x < position.x: + _crop.rect.position.x = _start_pos.x + else: + _crop.rect.position.x = _start_pos.x - _crop.rect.size.x + if _start_pos.y < position.y: + _crop.rect.position.y = _start_pos.y + else: + _crop.rect.position.y = _start_pos.y - _crop.rect.size.y + # Ensure that the size is at least 1: + _crop.rect.size.x = max(1, _crop.rect.size.x) + _crop.rect.size.y = max(1, _crop.rect.size.y) + _crop.emit_signal("updated") + + +func _sync_ui() -> void: + _syncing = true + $"%CropMode".selected = _crop.mode + $"%SizeLock".pressed = _crop.locked_size + match _crop.mode: + CropRect.Mode.MARGINS: + $"%MarginsContainer".show() + $"%RatioContainer".hide() + $"%PosSizeContainer".hide() + $"%DimensionsLabel".show() + CropRect.Mode.POSITION_SIZE: + $"%MarginsContainer".hide() + $"%RatioContainer".hide() + $"%PosSizeContainer".show() + $"%DimensionsLabel".hide() + CropRect.Mode.LOCKED_ASPECT_RATIO: + $"%MarginsContainer".hide() + $"%RatioContainer".show() + $"%PosSizeContainer".show() + $"%DimensionsLabel".hide() + + $"%Top".max_value = Global.current_project.size.y - 1 + $"%Bottom".max_value = Global.current_project.size.y + $"%Left".max_value = Global.current_project.size.x - 1 + $"%Right".max_value = Global.current_project.size.x + $"%Top".value = _crop.rect.position.y + $"%Bottom".value = _crop.rect.end.y + $"%Left".value = _crop.rect.position.x + $"%Right".value = _crop.rect.end.x + + $"%RatioX".value = _crop.ratio.x + $"%RatioY".value = _crop.ratio.y + + $"%PositionX".max_value = Global.current_project.size.x - 1 + $"%PositionY".max_value = Global.current_project.size.y - 1 + $"%Width".max_value = Global.current_project.size.x + $"%Height".max_value = Global.current_project.size.y + $"%PositionX".value = _crop.rect.position.x + $"%PositionY".value = _crop.rect.position.y + $"%Width".value = _crop.rect.size.x + $"%Height".value = _crop.rect.size.y + + $"%DimensionsLabel".text = str(_crop.rect.size.x, " x ", _crop.rect.size.y) + _syncing = false + + +# UI Signals: + + +func _on_CropMode_item_selected(index: int) -> void: + if _syncing: + return + _crop.mode = index + _crop.emit_signal("updated") + + +func _on_SizeLock_toggled(button_pressed: bool) -> void: + if button_pressed: + $"%SizeLock".icon = preload("res://assets/graphics/misc/locked_size.png") + else: + $"%SizeLock".icon = preload("res://assets/graphics/misc/unlocked_size.png") + if _syncing: + return + _crop.locked_size = button_pressed + _crop.emit_signal("updated") + + +func _on_Top_value_changed(value: float) -> void: + if _syncing: + return + var difference := value - _crop.rect.position.y + _crop.rect.size.y = max(1, _crop.rect.size.y - difference) + _crop.rect.position.y = value + _crop.emit_signal("updated") + + +func _on_Bottom_value_changed(value: float) -> void: + if _syncing: + return + _crop.rect.position.y = min(value - 1, _crop.rect.position.y) + _crop.rect.end.y = value + _crop.emit_signal("updated") + + +func _on_Left_value_changed(value: float) -> void: + if _syncing: + return + var difference := value - _crop.rect.position.x + _crop.rect.size.x = max(1, _crop.rect.size.x - difference) + _crop.rect.position.x = value + _crop.emit_signal("updated") + + +func _on_Right_value_changed(value: float) -> void: + if _syncing: + return + _crop.rect.position.x = min(value - 1, _crop.rect.position.x) + _crop.rect.end.x = value + _crop.emit_signal("updated") + + +func _on_RatioX_value_changed(value: float) -> void: + if _syncing: + return + _crop.rect.size.x = round(max(1, _crop.rect.size.y / _crop.ratio.y * value)) + _crop.ratio.x = value + _crop.emit_signal("updated") + + +func _on_RatioY_value_changed(value: float) -> void: + if _syncing: + return + _crop.rect.size.y = round(max(1, _crop.rect.size.x / _crop.ratio.x * value)) + _crop.ratio.y = value + _crop.emit_signal("updated") + + +func _on_PositionX_value_changed(value: float) -> void: + if _syncing: + return + _crop.rect.position.x = value + _crop.emit_signal("updated") + + +func _on_PositionY_value_changed(value: float) -> void: + if _syncing: + return + _crop.rect.position.y = value + _crop.emit_signal("updated") + + +func _on_Width_value_changed(value: float) -> void: + if _syncing: + return + if _crop.mode == CropRect.Mode.LOCKED_ASPECT_RATIO: + _crop.rect.size.y = round(max(1, (value / _crop.ratio.x) * _crop.ratio.y)) + _crop.rect.size.x = value + _crop.emit_signal("updated") + + +func _on_Height_value_changed(value: float) -> void: + if _syncing: + return + if _crop.mode == CropRect.Mode.LOCKED_ASPECT_RATIO: + _crop.rect.size.x = round(max(1, (value / _crop.ratio.y) * _crop.ratio.x)) + _crop.rect.size.y = value + _crop.emit_signal("updated") + + +func _on_Apply_pressed() -> void: + _crop.apply() diff --git a/src/Tools/CropTool.tscn b/src/Tools/CropTool.tscn new file mode 100644 index 000000000..a343c6bc4 --- /dev/null +++ b/src/Tools/CropTool.tscn @@ -0,0 +1,214 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://src/Tools/BaseTool.tscn" type="PackedScene" id=1] +[ext_resource path="res://src/UI/Nodes/ValueSlider.tscn" type="PackedScene" id=2] +[ext_resource path="res://src/Tools/CropTool.gd" type="Script" id=3] +[ext_resource path="res://assets/graphics/misc/unlocked_size.png" type="Texture" id=4] + +[node name="ToolOptions" instance=ExtResource( 1 )] +script = ExtResource( 3 ) + +[node name="ModeLabel" type="Label" parent="." index="4"] +margin_top = 26.0 +margin_right = 116.0 +margin_bottom = 40.0 +text = "Mode:" + +[node name="HBoxContainer" type="HBoxContainer" parent="." index="5"] +margin_top = 44.0 +margin_right = 116.0 +margin_bottom = 66.0 + +[node name="CropMode" type="OptionButton" parent="HBoxContainer" index="0"] +unique_name_in_owner = true +margin_right = 84.0 +margin_bottom = 22.0 +size_flags_horizontal = 3 +text = "Margins" +clip_text = true +items = [ "Margins", null, false, 0, null, "Position + Size", null, false, 1, null, "Locked Aspect Ratio", null, false, 3, null ] +selected = 0 + +[node name="SizeLock" type="Button" parent="HBoxContainer" index="1"] +unique_name_in_owner = true +margin_left = 88.0 +margin_right = 116.0 +margin_bottom = 22.0 +hint_tooltip = "Locked size + +When enabled using the tool on the canvas will only move the cropping rectangle. + +When disabled using the tool on the canvas will draw the rectangle." +focus_mode = 0 +toggle_mode = true +icon = ExtResource( 4 ) +__meta__ = { +"_editor_description_": "" +} + +[node name="MarginsContainer" type="VBoxContainer" parent="." index="6"] +unique_name_in_owner = true +margin_top = 70.0 +margin_right = 116.0 +margin_bottom = 196.0 + +[node name="MarginsLabel" type="Label" parent="MarginsContainer" index="0"] +margin_right = 116.0 +margin_bottom = 14.0 +text = "Margins:" + +[node name="Top" parent="MarginsContainer" index="1" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 18.0 +margin_right = 116.0 +margin_bottom = 42.0 +allow_greater = true +allow_lesser = true +prefix = "Top:" + +[node name="Bottom" parent="MarginsContainer" index="2" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 46.0 +margin_right = 116.0 +margin_bottom = 70.0 +allow_greater = true +allow_lesser = true +prefix = "Bottom:" + +[node name="Left" parent="MarginsContainer" index="3" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 74.0 +margin_right = 116.0 +margin_bottom = 98.0 +allow_greater = true +allow_lesser = true +prefix = "Left:" + +[node name="Right" parent="MarginsContainer" index="4" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 102.0 +margin_right = 116.0 +margin_bottom = 126.0 +allow_greater = true +allow_lesser = true +prefix = "Right:" + +[node name="RatioContainer" type="VBoxContainer" parent="." index="7"] +unique_name_in_owner = true +margin_top = 200.0 +margin_right = 116.0 +margin_bottom = 242.0 + +[node name="AspectRatioLabel" type="Label" parent="RatioContainer" index="0"] +margin_right = 116.0 +margin_bottom = 14.0 +text = "Aspect Ratio:" + +[node name="HBoxContainer" type="HBoxContainer" parent="RatioContainer" index="1"] +margin_top = 18.0 +margin_right = 116.0 +margin_bottom = 42.0 + +[node name="RatioX" parent="RatioContainer/HBoxContainer" index="0" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_right = 52.0 +min_value = 1.0 +value = 1.0 +allow_greater = true + +[node name="Label" type="Label" parent="RatioContainer/HBoxContainer" index="1"] +margin_left = 56.0 +margin_top = 5.0 +margin_right = 60.0 +margin_bottom = 19.0 +text = ":" + +[node name="RatioY" parent="RatioContainer/HBoxContainer" index="2" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_left = 64.0 +margin_right = 116.0 +min_value = 1.0 +value = 1.0 +allow_greater = true + +[node name="PosSizeContainer" type="VBoxContainer" parent="." index="8"] +unique_name_in_owner = true +margin_top = 246.0 +margin_right = 116.0 +margin_bottom = 390.0 + +[node name="PositionLabel" type="Label" parent="PosSizeContainer" index="0"] +margin_right = 116.0 +margin_bottom = 14.0 +text = "Position:" + +[node name="PositionX" parent="PosSizeContainer" index="1" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 18.0 +margin_right = 116.0 +margin_bottom = 42.0 +allow_greater = true +allow_lesser = true +prefix = "X:" + +[node name="PositionY" parent="PosSizeContainer" index="2" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 46.0 +margin_right = 116.0 +margin_bottom = 70.0 +allow_greater = true +allow_lesser = true +prefix = "Y:" + +[node name="SizeLabel" type="Label" parent="PosSizeContainer" index="3"] +margin_top = 74.0 +margin_right = 116.0 +margin_bottom = 88.0 +text = "Size:" + +[node name="Width" parent="PosSizeContainer" index="4" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 92.0 +margin_right = 116.0 +margin_bottom = 116.0 +min_value = 1.0 +value = 1.0 +allow_greater = true +prefix = "W:" + +[node name="Height" parent="PosSizeContainer" index="5" instance=ExtResource( 2 )] +unique_name_in_owner = true +margin_top = 120.0 +margin_right = 116.0 +margin_bottom = 144.0 +min_value = 1.0 +value = 1.0 +allow_greater = true +prefix = "H:" + +[node name="Apply" type="Button" parent="." index="9"] +margin_top = 394.0 +margin_right = 116.0 +margin_bottom = 414.0 +text = "Apply" + +[node name="DimensionsLabel" type="Label" parent="." index="10"] +unique_name_in_owner = true +margin_top = 418.0 +margin_right = 116.0 +margin_bottom = 432.0 +align = 1 + +[connection signal="item_selected" from="HBoxContainer/CropMode" to="." method="_on_CropMode_item_selected"] +[connection signal="toggled" from="HBoxContainer/SizeLock" to="." method="_on_SizeLock_toggled"] +[connection signal="value_changed" from="MarginsContainer/Top" to="." method="_on_Top_value_changed"] +[connection signal="value_changed" from="MarginsContainer/Bottom" to="." method="_on_Bottom_value_changed"] +[connection signal="value_changed" from="MarginsContainer/Left" to="." method="_on_Left_value_changed"] +[connection signal="value_changed" from="MarginsContainer/Right" to="." method="_on_Right_value_changed"] +[connection signal="value_changed" from="RatioContainer/HBoxContainer/RatioX" to="." method="_on_RatioX_value_changed"] +[connection signal="value_changed" from="RatioContainer/HBoxContainer/RatioY" to="." method="_on_RatioY_value_changed"] +[connection signal="value_changed" from="PosSizeContainer/PositionX" to="." method="_on_PositionX_value_changed"] +[connection signal="value_changed" from="PosSizeContainer/PositionY" to="." method="_on_PositionY_value_changed"] +[connection signal="value_changed" from="PosSizeContainer/Width" to="." method="_on_Width_value_changed"] +[connection signal="value_changed" from="PosSizeContainer/Height" to="." method="_on_Height_value_changed"] +[connection signal="pressed" from="Apply" to="." method="_on_Apply_pressed"] diff --git a/src/UI/Canvas/Canvas.gd b/src/UI/Canvas/Canvas.gd index b46db3bb0..980f44a77 100644 --- a/src/UI/Canvas/Canvas.gd +++ b/src/UI/Canvas/Canvas.gd @@ -14,6 +14,7 @@ onready var tile_mode = $TileMode onready var pixel_grid = $PixelGrid onready var grid = $Grid onready var selection = $Selection +onready var crop_rect: CropRect = $CropRect onready var indicators = $Indicators onready var previews = $Previews onready var mouse_guide_container = $MouseGuideContainer diff --git a/src/UI/Canvas/Canvas.tscn b/src/UI/Canvas/Canvas.tscn index 905f96776..a3ebbebe3 100644 --- a/src/UI/Canvas/Canvas.tscn +++ b/src/UI/Canvas/Canvas.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=16 format=2] +[gd_scene load_steps=17 format=2] [ext_resource path="res://src/UI/Canvas/Canvas.gd" type="Script" id=1] [ext_resource path="res://src/UI/Canvas/Grid.gd" type="Script" id=2] @@ -12,6 +12,7 @@ [ext_resource path="res://src/Shaders/AutoInvertColors.shader" type="Shader" id=10] [ext_resource path="res://src/UI/Canvas/MouseGuideContainer.tscn" type="PackedScene" id=11] [ext_resource path="res://src/UI/Canvas/OnionSkinning.gd" type="Script" id=12] +[ext_resource path="res://src/UI/Canvas/CropRect.gd" type="Script" id=13] [sub_resource type="CanvasItemMaterial" id=1] blend_mode = 4 @@ -61,6 +62,10 @@ script = ExtResource( 8 ) material = SubResource( 2 ) centered = false +[node name="CropRect" type="Node2D" parent="."] +visible = false +script = ExtResource( 13 ) + [node name="Indicators" type="Node2D" parent="."] script = ExtResource( 3 ) diff --git a/src/UI/Canvas/CropRect.gd b/src/UI/Canvas/CropRect.gd new file mode 100644 index 000000000..fc9447f76 --- /dev/null +++ b/src/UI/Canvas/CropRect.gd @@ -0,0 +1,97 @@ +class_name CropRect +extends Node2D +# Draws the rectangle overlay for the crop tool +# Stores the shared settings between left and right crop tools + +signal updated + +enum Mode { MARGINS, POSITION_SIZE, LOCKED_ASPECT_RATIO } + +const BIG = 100000 # Size of big rectangles used to darken background. +const DARKEN_COLOR = Color(0, 0, 0, 0.5) +const LINE_COLOR = Color.white + +var mode := 0 setget _set_mode +var locked_size := false +var rect := Rect2(0, 0, 1, 1) +var ratio := Vector2.ONE + +# How many crop tools are active (0-2), setter makes this visible if not 0 +var tool_count := 0 setget _set_tool_count + + +func _ready() -> void: + connect("updated", self, "update") + Global.connect("project_changed", self, "reset") + mode = Global.config_cache.get_value("preferences", "crop_mode", 0) + locked_size = Global.config_cache.get_value("preferences", "crop_locked_size", false) + reset() + + +func _exit_tree(): + Global.config_cache.set_value("preferences", "crop_mode", mode) + Global.config_cache.set_value("preferences", "crop_locked_size", locked_size) + + +func _draw() -> void: + # Darken the background by drawing big rectangles around it (top/bottomm/left/right): + draw_rect( + Rect2(rect.position.x - BIG, rect.position.y - BIG, BIG * 2 + rect.size.x, BIG), + DARKEN_COLOR + ) + draw_rect(Rect2(rect.position.x - BIG, rect.end.y, BIG * 2 + rect.size.x, BIG), DARKEN_COLOR) + draw_rect(Rect2(rect.position.x - BIG, rect.position.y, BIG, rect.size.y), DARKEN_COLOR) + draw_rect(Rect2(rect.end.x, rect.position.y, BIG, rect.size.y), DARKEN_COLOR) + + # Rect: + draw_rect(rect, LINE_COLOR, false) + + # Horizontal rule of thirds lines: + var third: float = rect.position.y + rect.size.y * 0.333 + draw_line(Vector2(rect.position.x, third), Vector2(rect.end.x, third), LINE_COLOR) + third = rect.position.y + rect.size.y * 0.667 + draw_line(Vector2(rect.position.x, third), Vector2(rect.end.x, third), LINE_COLOR) + + # Vertical rule of thirds lines: + third = rect.position.x + rect.size.x * 0.333 + draw_line(Vector2(third, rect.position.y), Vector2(third, rect.end.y), LINE_COLOR) + third = rect.position.x + rect.size.x * 0.667 + draw_line(Vector2(third, rect.position.y), Vector2(third, rect.end.y), LINE_COLOR) + + +func apply() -> void: + DrawingAlgos.resize_canvas(rect.size.x, rect.size.y, -rect.position.x, -rect.position.y) + + +func reset() -> void: + rect.position = Vector2.ZERO + rect.size = Global.current_project.size + if mode == Mode.LOCKED_ASPECT_RATIO: + _auto_ratio_from_resolution() + emit_signal("updated") + + +func _auto_ratio_from_resolution() -> void: + var divisor := _gcd(rect.size.x, rect.size.y) + ratio = rect.size / divisor + + +# Greatest common divisor +func _gcd(a: int, b: int) -> int: + return a if b == 0 else _gcd(b, a % b) + + +# Setters + + +func _set_mode(value: int) -> void: + if value == Mode.LOCKED_ASPECT_RATIO and mode != Mode.LOCKED_ASPECT_RATIO: + _auto_ratio_from_resolution() + mode = value + + +func _set_tool_count(value: int) -> void: + if tool_count == 0 and value > 0: + reset() # Reset once 1 tool becomes the crop tool + tool_count = value + visible = tool_count diff --git a/src/UI/Timeline/AnimationTimeline.gd b/src/UI/Timeline/AnimationTimeline.gd index 3808b4ae0..a388273f6 100644 --- a/src/UI/Timeline/AnimationTimeline.gd +++ b/src/UI/Timeline/AnimationTimeline.gd @@ -69,7 +69,6 @@ func _frame_scroll_changed(value: float) -> void: func _on_LayerVBox_resized() -> void: - # TODO: BUG Layers resizing doesn't update the tags! frame_scroll_bar.margin_left = frame_scroll_container.rect_position.x tag_spacer.rect_min_size.x = ( frame_scroll_container.rect_global_position.x