mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-18 17:19:50 +00:00
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 <MrTriPie>
This commit is contained in:
parent
e008c39286
commit
0ad86816e5
|
@ -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 ""
|
BIN
assets/graphics/misc/locked_size.png
Normal file
BIN
assets/graphics/misc/locked_size.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 175 B |
35
assets/graphics/misc/locked_size.png.import
Normal file
35
assets/graphics/misc/locked_size.png.import
Normal file
|
@ -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
|
BIN
assets/graphics/misc/unlocked_size.png
Normal file
BIN
assets/graphics/misc/unlocked_size.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 173 B |
35
assets/graphics/misc/unlocked_size.png.import
Normal file
35
assets/graphics/misc/unlocked_size.png.import
Normal file
|
@ -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
|
BIN
assets/graphics/tools/crop.png
Normal file
BIN
assets/graphics/tools/crop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 B |
35
assets/graphics/tools/crop.png.import
Normal file
35
assets/graphics/tools/crop.png.import
Normal file
|
@ -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
|
BIN
assets/graphics/tools/cursors/crop.png
Normal file
BIN
assets/graphics/tools/cursors/crop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 226 B |
35
assets/graphics/tools/cursors/crop.png.import
Normal file
35
assets/graphics/tools/cursors/crop.png.import
Normal file
|
@ -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
|
|
@ -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": "",
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
210
src/Tools/CropTool.gd
Normal file
210
src/Tools/CropTool.gd
Normal file
|
@ -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()
|
214
src/Tools/CropTool.tscn
Normal file
214
src/Tools/CropTool.tscn
Normal file
|
@ -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"]
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
||||
|
|
97
src/UI/Canvas/CropRect.gd
Normal file
97
src/UI/Canvas/CropRect.gd
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue