From 0ad86816e54c873c308cc534784fdcb8135a9e76 Mon Sep 17 00:00:00 2001 From: mrtripie <65431647+mrtripie@users.noreply.github.com> Date: Thu, 16 Mar 2023 13:50:45 -0400 Subject: [PATCH] Crop Tool (#830) * Added intial crop tool * [skip ci] Removed unneeeded LINE_WIDTH constant from CropRect * Added DimensionsLabel * Cleaned up CropRect._draw * Hide the CropRect after switching to a different tool * Sets the crop values to the entire canvas size on setup * [skip ci] Added crop tool tooltip * Made Crop tools synced with eachother by placing the crop settings on CropRect * Added new modes, refactored, added rule of thirds lines and darkened background * [skip ci] reset optimization * Renames * [skip ci] Tweaked the darkened background * Fixed bug with top/bottom/left/right sliders after rect refactor * Changing width/height on locked aspect ratio bug * Aspect ratio sliders rounding/setting a min of 1 px height/width * Fixed bugs with drawing the crop rect, especially with locked aspect ratio * Save the mode to config_cache * Added size lock and renamed mode * Added tooltip for size lock. Not sure if I did the translation file right * removed signal that wasn't used * Formatting * Removed old TODO comment from one of my previous PRs that isn't true anymore * Fixed definition order for linter * Changed locked size modes moving to offset by @Variable-ind, reordered methods --------- Co-authored-by: MrTriPie --- Translations/Translations.pot | 38 ++++ assets/graphics/misc/locked_size.png | Bin 0 -> 175 bytes assets/graphics/misc/locked_size.png.import | 35 +++ assets/graphics/misc/unlocked_size.png | Bin 0 -> 173 bytes assets/graphics/misc/unlocked_size.png.import | 35 +++ assets/graphics/tools/crop.png | Bin 0 -> 184 bytes assets/graphics/tools/crop.png.import | 35 +++ assets/graphics/tools/cursors/crop.png | Bin 0 -> 226 bytes assets/graphics/tools/cursors/crop.png.import | 35 +++ project.godot | 6 + src/Autoload/Global.gd | 6 + src/Autoload/Tools.gd | 2 + src/Classes/Project.gd | 1 + src/Tools/CropTool.gd | 210 +++++++++++++++++ src/Tools/CropTool.tscn | 214 ++++++++++++++++++ src/UI/Canvas/Canvas.gd | 1 + src/UI/Canvas/Canvas.tscn | 7 +- src/UI/Canvas/CropRect.gd | 97 ++++++++ src/UI/Timeline/AnimationTimeline.gd | 1 - 19 files changed, 721 insertions(+), 2 deletions(-) create mode 100644 assets/graphics/misc/locked_size.png create mode 100644 assets/graphics/misc/locked_size.png.import create mode 100644 assets/graphics/misc/unlocked_size.png create mode 100644 assets/graphics/misc/unlocked_size.png.import create mode 100644 assets/graphics/tools/crop.png create mode 100644 assets/graphics/tools/crop.png.import create mode 100644 assets/graphics/tools/cursors/crop.png create mode 100644 assets/graphics/tools/cursors/crop.png.import create mode 100644 src/Tools/CropTool.gd create mode 100644 src/Tools/CropTool.tscn create mode 100644 src/UI/Canvas/CropRect.gd 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 0000000000000000000000000000000000000000..cc0ff727902dd422d9e1a794cf8bbbee4c647cd8 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|GCf@!Lo9mN z21oN9P~d60`~TdVbi>&AW2JvYy;VaR6*(9tZQ(E~nCAa*c5Q4SLxJqiwQO6ynQA0hQb9gh2A2hF9E|buDcS8NP2?w8DOf@e1P}w`lWzs=M@rbgrKTMnZ%@6Tj XzH1>`G>PM+_eM3%|$?5K8Y@2+!MY%3E zUG5u~R@@oiApQ#F)sbc!j;uLpAKj7g_rxB{unl|NJe53`D;VpX+8Uqm_3_j~#xHpW Va+W$13V>EJc)I$ztaD0e0suBzJnR4f literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b6ea40772f21e9a32928ec10c09864f32b947e8e GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fjKx9jP7LeL$-D$|iacE$Lo9mt zUNPiraNuA$aP-H2{p94vHtPh(?J9{4Y!CWZtg1^YNxWTOrC{ygqB19<^WMg-&5id~ zY|rG5Tc}&neyN}(sDMrPaO4)2@Czm}uE7S)Z3f+W5BYYr>|Vj`_a}v+BFD7tR<`>5 fKN41{ONtmHJoBHuTIRzIw4K4z)z4*}Q$iB}TjN5^ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6dcb0445aa86a631810c301a09e5bc1c1b80dd05 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fjKx9jP7LeL$-D$|=6Jd|hFJ8j zz3Rx9nPD@~Yr6#9hl$-3-s4g>at^h;d%0scjdw8@S$!-f%;1pzJ z2-N<;Z)GULP@-{X!QSw)SBtkXJ$sy 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