From 7b8c6bbf000752cafa33135f70a6365e7f282a05 Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Tue, 29 Oct 2019 23:22:38 +0200 Subject: [PATCH] UndoRedo - Unstable with bugs Started working on UndoRedo. Currently works with basic drawing/erasing/bucket filling as well as the rectangle selection tool, custom brushes and copying/pasting. May not work with multiple layers and frames and it does not work with the rest of the tools and buttons. Also does not work when pressing both mouse buttons at the same time, or when the cursor is outside the canvas when drawing. --- Main.tscn | 101 ++++++++++++-------------- Scripts/BrushButton.gd | 2 +- Scripts/Canvas.gd | 130 ++++++++++++++++++++++------------ Scripts/Global.gd | 32 ++++++--- Scripts/LayerContainer.gd | 4 +- Scripts/Main.gd | 100 +++++++++++++------------- Scripts/SelectionRectangle.gd | 32 +++++---- export_presets.cfg | 2 +- project.godot | 10 --- 9 files changed, 227 insertions(+), 186 deletions(-) diff --git a/Main.tscn b/Main.tscn index e5f17182e..3dfe20ea0 100644 --- a/Main.tscn +++ b/Main.tscn @@ -16,13 +16,13 @@ anchor_bottom = 1.0 script = ExtResource( 1 ) [node name="UI" type="HBoxContainer" parent="."] +editor/display_folded = true anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 custom_constants/separation = 0 [node name="ToolPanel" type="Panel" parent="UI"] -editor/display_folded = true margin_right = 230.0 margin_bottom = 600.0 rect_min_size = Vector2( 230, 0 ) @@ -35,13 +35,13 @@ size_flags_horizontal = 3 size_flags_vertical = 3 [node name="MenusAndTools" type="VBoxContainer" parent="UI/ToolPanel/Tools"] -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 266.0 size_flags_vertical = 3 [node name="MenuItems" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] editor/display_folded = true -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 20.0 [node name="FileMenu" type="MenuButton" parent="UI/ToolPanel/Tools/MenusAndTools/MenuItems"] @@ -72,13 +72,11 @@ mouse_default_cursor_shape = 2 text = "Help" [node name="PaintToolsContainer" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] -editor/display_folded = true margin_top = 24.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 44.0 [node name="Pencil" type="Button" parent="UI/ToolPanel/Tools/MenusAndTools/PaintToolsContainer"] -editor/display_folded = true margin_right = 51.0 margin_bottom = 20.0 hint_tooltip = "P for left mouse button @@ -94,7 +92,6 @@ centered = false offset = Vector2( 0, -10 ) [node name="Eraser" type="Button" parent="UI/ToolPanel/Tools/MenusAndTools/PaintToolsContainer"] -editor/display_folded = true margin_left = 55.0 margin_right = 106.0 margin_bottom = 20.0 @@ -121,9 +118,8 @@ button_mask = 3 text = "Bucket" [node name="ColorToolsContainer" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] -editor/display_folded = true margin_top = 48.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 68.0 [node name="PaintAllPixelsSameColor" type="Button" parent="UI/ToolPanel/Tools/MenusAndTools/ColorToolsContainer"] @@ -148,9 +144,8 @@ button_mask = 3 text = "Lighten/Darken" [node name="SelectionToolsContainer2" type="HBoxContainer" parent="UI/ToolPanel/Tools/MenusAndTools"] -editor/display_folded = true margin_top = 72.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 92.0 [node name="RectSelect" type="Button" parent="UI/ToolPanel/Tools/MenusAndTools/SelectionToolsContainer2"] @@ -165,30 +160,29 @@ text = "RectSelect" [node name="HSeparator" type="HSeparator" parent="UI/ToolPanel/Tools"] margin_top = 270.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 274.0 [node name="ToolOptions" type="HBoxContainer" parent="UI/ToolPanel/Tools"] -editor/display_folded = true margin_top = 278.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 544.0 size_flags_vertical = 3 custom_constants/separation = 0 [node name="LeftToolOptions" type="VBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions"] -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 266.0 size_flags_horizontal = 3 [node name="LeftLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 14.0 text = "Left tool options" [node name="LeftIndicatorCheckbox" type="CheckBox" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] margin_top = 18.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 42.0 hint_tooltip = "Show left mouse indicator when drawing" mouse_default_cursor_shape = 2 @@ -207,13 +201,13 @@ size_flags_vertical = 0 [node name="BrushSizeLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] margin_top = 82.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 96.0 text = "Brush size: " [node name="LeftBrushSizeEdit" type="SpinBox" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] margin_top = 100.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 124.0 mouse_default_cursor_shape = 2 min_value = 1.0 @@ -222,14 +216,14 @@ suffix = "px" [node name="ColorComesFrom" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] margin_top = 128.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 142.0 text = "Brush color from" [node name="InterpolateColor" type="HBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] editor/display_folded = true margin_top = 146.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 162.0 [node name="BrushColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/InterpolateColor"] @@ -241,7 +235,7 @@ text = "B" [node name="LeftInterpolateFactor" type="HSlider" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/InterpolateColor"] margin_left = 13.0 -margin_right = 107.0 +margin_right = 101.0 margin_bottom = 16.0 hint_tooltip = "Choose if the brush's color should come from the brush itself (left), or the currently selected color (right)" size_flags_horizontal = 3 @@ -251,44 +245,44 @@ value = 0.5 ticks_on_borders = true [node name="SelectedColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions/InterpolateColor"] -margin_left = 111.0 +margin_left = 105.0 margin_top = 1.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 15.0 rect_pivot_offset = Vector2( -90, -47 ) text = "C" [node name="LeftHorizontalMirroring" type="CheckBox" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] margin_top = 166.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 190.0 text = "Horiz. Mirror" [node name="LeftVerticalMirroring" type="CheckBox" parent="UI/ToolPanel/Tools/ToolOptions/LeftToolOptions"] margin_top = 194.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 218.0 text = "Vert. Mirror" [node name="VSeparator" type="VSeparator" parent="UI/ToolPanel/Tools/ToolOptions"] -margin_left = 119.0 -margin_right = 123.0 +margin_left = 113.0 +margin_right = 117.0 margin_bottom = 266.0 [node name="RightToolOptions" type="VBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions"] -margin_left = 123.0 -margin_right = 242.0 +margin_left = 117.0 +margin_right = 230.0 margin_bottom = 266.0 size_flags_horizontal = 3 [node name="RightLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 14.0 text = "Right tool options" [node name="RightIndicatorCheckbox" type="CheckBox" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] margin_top = 18.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 42.0 hint_tooltip = "Show right mouse indicator when drawing" mouse_default_cursor_shape = 2 @@ -306,13 +300,13 @@ size_flags_vertical = 0 [node name="BrushSizeLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] margin_top = 82.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 96.0 text = "Brush size: " [node name="RightBrushSizeEdit" type="SpinBox" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] margin_top = 100.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 124.0 mouse_default_cursor_shape = 2 min_value = 1.0 @@ -321,14 +315,14 @@ suffix = "px" [node name="ColorComesFrom" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] margin_top = 128.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 142.0 text = "Brush color from" [node name="InterpolateColor" type="HBoxContainer" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] editor/display_folded = true margin_top = 146.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 162.0 [node name="BrushColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/InterpolateColor"] @@ -340,7 +334,7 @@ text = "B" [node name="RightInterpolateFactor" type="HSlider" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/InterpolateColor"] margin_left = 13.0 -margin_right = 107.0 +margin_right = 101.0 margin_bottom = 16.0 hint_tooltip = "Choose if the brush's color should come from the brush itself (left), or the currently selected color (right)" size_flags_horizontal = 3 @@ -350,34 +344,33 @@ value = 0.5 ticks_on_borders = true [node name="SelectedColorLabel" type="Label" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions/InterpolateColor"] -margin_left = 111.0 +margin_left = 105.0 margin_top = 1.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 15.0 rect_pivot_offset = Vector2( -90, -47 ) text = "C" [node name="RightHorizontalMirroring" type="CheckBox" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] margin_top = 166.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 190.0 text = "Horiz. Mirror" [node name="RightVerticalMirroring" type="CheckBox" parent="UI/ToolPanel/Tools/ToolOptions/RightToolOptions"] margin_top = 194.0 -margin_right = 119.0 +margin_right = 113.0 margin_bottom = 218.0 text = "Vert. Mirror" [node name="HSeparator2" type="HSeparator" parent="UI/ToolPanel/Tools"] margin_top = 548.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 552.0 [node name="BrushesContainer" type="ScrollContainer" parent="UI/ToolPanel/Tools"] -editor/display_folded = true margin_top = 556.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 592.0 size_flags_horizontal = 3 scroll_vertical_enabled = false @@ -402,7 +395,7 @@ offset = Vector2( 28, 0 ) [node name="HSeparator3" type="HSeparator" parent="UI/ToolPanel/Tools"] margin_top = 596.0 -margin_right = 242.0 +margin_right = 230.0 margin_bottom = 600.0 [node name="CanvasAndTimeline" type="VBoxContainer" parent="UI"] @@ -412,6 +405,7 @@ margin_bottom = 600.0 size_flags_horizontal = 3 [node name="HBoxContainer" type="HBoxContainer" parent="UI/CanvasAndTimeline"] +editor/display_folded = true margin_right = 634.0 margin_bottom = 464.0 size_flags_horizontal = 3 @@ -472,7 +466,6 @@ zoom = Vector2( 0.15, 0.15 ) script = ExtResource( 6 ) [node name="AnimationTimeline" type="Panel" parent="UI/CanvasAndTimeline"] -editor/display_folded = true margin_top = 468.0 margin_right = 634.0 margin_bottom = 600.0 @@ -868,8 +861,8 @@ resizable = true mode = 0 access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "/home/danielnaoexiste/Documents/Prog/Pixelorama" -current_path = "/home/danielnaoexiste/Documents/Prog/Pixelorama/" +current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" [node name="SaveSprite" type="FileDialog" parent="."] anchor_left = 0.5 @@ -883,8 +876,8 @@ margin_bottom = 48.0 resizable = true access = 2 filters = PoolStringArray( "*.pxo ; Pixelorama Project" ) -current_dir = "/home/danielnaoexiste/Documents/Prog/Pixelorama" -current_path = "/home/danielnaoexiste/Documents/Prog/Pixelorama/" +current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" [node name="ImportSprites" type="FileDialog" parent="."] margin_right = 515.0 @@ -894,8 +887,8 @@ resizable = true mode = 1 access = 2 filters = PoolStringArray( "*jpg, *.png ; JPG, PNG Images" ) -current_dir = "/home/danielnaoexiste/Documents/Prog/Pixelorama" -current_path = "/home/danielnaoexiste/Documents/Prog/Pixelorama/" +current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" [node name="ExportSprites" type="FileDialog" parent="."] anchor_left = 0.5 @@ -910,8 +903,8 @@ window_title = "Export sprite" resizable = true access = 2 filters = PoolStringArray( "*.png ; PNG Image" ) -current_dir = "/home/danielnaoexiste/Documents/Prog/Pixelorama" -current_path = "/home/danielnaoexiste/Documents/Prog/Pixelorama/" +current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" [node name="ScaleImage" type="ConfirmationDialog" parent="."] editor/display_folded = true diff --git a/Scripts/BrushButton.gd b/Scripts/BrushButton.gd index d82bea53f..dc026daa9 100644 --- a/Scripts/BrushButton.gd +++ b/Scripts/BrushButton.gd @@ -11,7 +11,7 @@ func _on_BrushButton_pressed() -> void: if custom_brush_index > -1: Global.custom_left_brush_index = custom_brush_index Global.update_left_custom_brush() - + elif Input.is_action_just_released("right_mouse"): Global.current_right_brush_type = brush_type Global.right_brush_indicator.get_parent().remove_child(Global.right_brush_indicator) diff --git a/Scripts/Canvas.gd b/Scripts/Canvas.gd index 25e5c6ff2..11b544452 100644 --- a/Scripts/Canvas.gd +++ b/Scripts/Canvas.gd @@ -11,6 +11,7 @@ var frame_button : VBoxContainer var frame_texture_rect : TextureRect var previous_mouse_pos := Vector2.ZERO +var previous_action := "None" var mouse_inside_canvas := false #used for undo var sprite_changed_this_frame := false #for optimization purposes @@ -24,30 +25,30 @@ func _ready() -> void: #Background trans_background = ImageTexture.new() trans_background.create_from_image(load("res://Assets/Graphics/Transparent Background.png"), 0) - + #The sprite itself if layers.empty(): var sprite := Image.new() sprite.create(size.x, size.y, false, Image.FORMAT_RGBA8) - + sprite.lock() var tex := ImageTexture.new() tex.create_from_image(sprite, 0) - + #Store [Image, ImageTexture, Layer Name, Visibity boolean] layers.append([sprite, tex, "Layer 0", true]) - + generate_layer_panels() - + frame_button = load("res://Prefabs/FrameButton.tscn").instance() frame_button.name = "Frame_%s" % frame frame_button.get_node("FrameButton").frame = frame frame_button.get_node("FrameID").text = str(frame + 1) Global.frame_container.add_child(frame_button) - + frame_texture_rect = Global.find_node_by_name(frame_button, "FrameTexture") frame_texture_rect.texture = layers[0][1] #ImageTexture current_layer_index - + camera_zoom() func camera_zoom() -> void: @@ -82,7 +83,7 @@ func _process(delta) -> void: elif Input.is_mouse_button_pressed(BUTTON_RIGHT): current_mouse_button = "right_mouse" current_action = Global.current_right_tool - + if visible: if !point_in_rectangle(mouse_pos, location, location + size): if !Input.is_mouse_button_pressed(BUTTON_LEFT) && !Input.is_mouse_button_pressed(BUTTON_RIGHT): @@ -91,8 +92,22 @@ func _process(delta) -> void: Global.cursor_position_label.text = "[%sx%s]" % [size.x, size.y] else: Global.cursor_position_label.text = "[%sx%s] %s, %s" % [size.x, size.y, mouse_pos_floored.x, mouse_pos_floored.y] - #Handle current tool - match current_action: + + + #Handle Undo/Redo + if point_in_rectangle(mouse_pos, location, location + size) && Global.can_draw && Global.has_focus: + if Input.is_action_just_pressed("left_mouse") || Input.is_action_just_pressed("right_mouse"): + if current_action != "None": + if current_action == "RectSelect": + handle_undo("Rectangle Select") + else: + handle_undo("Draw") + + elif Input.is_action_just_released("left_mouse") || Input.is_action_just_released("right_mouse"): + if previous_action != "None" && previous_action != "RectSelect": + handle_redo("Draw") + + match current_action: #Handle current tool "Pencil": var current_color : Color if current_mouse_button == "left_mouse": @@ -117,7 +132,7 @@ func _process(delta) -> void: current_color = Global.right_color_picker.color horizontal_mirror = Global.right_horizontal_mirror vertical_mirror = Global.right_vertical_mirror - + flood_fill(mouse_pos, layers[current_layer_index][0].get_pixelv(mouse_pos), current_color) if horizontal_mirror: var pos := Vector2(mirror_x, mouse_pos.y) @@ -128,15 +143,15 @@ func _process(delta) -> void: if horizontal_mirror && vertical_mirror: var pos := Vector2(mirror_x, mirror_y) flood_fill(pos, layers[current_layer_index][0].get_pixelv(pos), current_color) - + "PaintAllPixelsSameColor": - if point_in_rectangle(mouse_pos, location, location + size) && Global.current_frame == frame: + if point_in_rectangle(mouse_pos, location, location + size) && Global.can_draw && Global.has_focus && Global.current_frame == frame: var current_color : Color if current_mouse_button == "left_mouse": current_color = Global.left_color_picker.color elif current_mouse_button == "right_mouse": current_color = Global.right_color_picker.color - + var pixel_color : Color = layers[current_layer_index][0].get_pixelv(mouse_pos) for xx in size.x: for yy in size.y: @@ -145,7 +160,7 @@ func _process(delta) -> void: layers[current_layer_index][0].set_pixel(xx, yy, current_color) sprite_changed_this_frame = true "LightenDarken": - if point_in_rectangle(mouse_pos, location, location + size): + if point_in_rectangle(mouse_pos, location, location + size) && Global.can_draw && Global.has_focus && Global.current_frame == frame: var pixel_color : Color = layers[current_layer_index][0].get_pixelv(mouse_pos) var amount := 0.05 var color_changed := pixel_color.lightened(amount) @@ -177,14 +192,14 @@ func _process(delta) -> void: Global.selection_rectangle.polygon[1] = Vector2(end_pos.x, start_pos.y) Global.selection_rectangle.polygon[2] = end_pos Global.selection_rectangle.polygon[3] = Vector2(start_pos.x, end_pos.y) - + if !is_making_line: previous_mouse_pos = mouse_pos previous_mouse_pos.x = clamp(previous_mouse_pos.x, location.x, location.x + size.x) previous_mouse_pos.y = clamp(previous_mouse_pos.y, location.y, location.y + size.y) else: line_2d.set_point_position(1, mouse_pos) - + if is_making_selection != "None": #If we're making a selection if Input.is_action_just_released(is_making_selection): #Finish selection when button is released var start_pos = Global.selection_rectangle.polygon[0] @@ -193,30 +208,54 @@ func _process(delta) -> void: var temp = end_pos.x end_pos.x = start_pos.x start_pos.x = temp - + if start_pos.y > end_pos.y: var temp = end_pos.y end_pos.y = start_pos.y start_pos.y = temp - + Global.selection_rectangle.polygon[0] = start_pos Global.selection_rectangle.polygon[1] = Vector2(end_pos.x, start_pos.y) Global.selection_rectangle.polygon[2] = end_pos Global.selection_rectangle.polygon[3] = Vector2(start_pos.x, end_pos.y) - + for xx in range(start_pos.x, end_pos.x): for yy in range(start_pos.y, end_pos.y): Global.selected_pixels.append(Vector2(xx, yy)) is_making_selection = "None" - + handle_redo("Rectangle Select") + + previous_action = current_action if sprite_changed_this_frame: update_texture(current_layer_index) - +func handle_undo(action : String) -> void: + #I'm not sure why I have to unlock it, but... + #...if I don't, it doesn't work properly + layers[current_layer_index][0].unlock() + var data = layers[current_layer_index][0].data + layers[current_layer_index][0].lock() + Global.undo_redo.create_action(action) + Global.undo_redo.add_undo_property(layers[current_layer_index][0], "data", data) + if action == "Rectangle Select": + var selected_pixels = Global.selected_pixels.duplicate() + Global.undo_redo.add_undo_property(Global.selection_rectangle, "polygon", Global.selection_rectangle.polygon) + Global.undo_redo.add_undo_property(Global, "selected_pixels", selected_pixels) + Global.undo_redo.add_undo_method(Global, "undo", self, current_layer_index) + +func handle_redo(action : String) -> void: + Global.undo_redo.add_do_property(layers[current_layer_index][0], "data", layers[current_layer_index][0].data) + if action == "Rectangle Select": + Global.undo_redo.add_do_property(Global.selection_rectangle, "polygon", Global.selection_rectangle.polygon) + Global.undo_redo.add_do_property(Global, "selected_pixels", Global.selected_pixels) + Global.undo_redo.add_do_method(Global, "redo", self, current_layer_index) + Global.undo_redo.commit_action() + print("Do: ", Global.undo_redo.get_current_action_name()) + func update_texture(layer_index : int) -> void: layers[layer_index][1].create_from_image(layers[layer_index][0], 0) get_layer_container(layer_index).get_child(0).get_child(1).texture = layers[layer_index][1] - + #This code is used to update the texture in the animation timeline frame button #but blend_rect causes major performance issues on large images var whole_image := Image.new() @@ -249,7 +288,7 @@ func _draw() -> void: for texture in Global.canvases[Global.current_frame - i].layers: color.a = 0.6/i draw_texture(texture[1], location, color) - + #Future if Global.onion_skinning_future_rate > 0: var color : Color @@ -262,12 +301,12 @@ func _draw() -> void: for texture in Global.canvases[Global.current_frame + i].layers: color.a = 0.6/i draw_texture(texture[1], location, color) - + #Draw current frame layers for texture in layers: if texture[3]: #if it's visible draw_texture(texture[1], location) - + if Global.tile_mode: draw_texture(texture[1], Vector2(location.x, location.y + size.y)) #Down draw_texture(texture[1], Vector2(location.x - size.x, location.y + size.y)) #Down Left @@ -277,14 +316,14 @@ func _draw() -> void: draw_texture(texture[1], Vector2(location.x + size.x, location.y - size.y)) #Up right draw_texture(texture[1], Vector2(location.x + size.x, location.y)) #Right draw_texture(texture[1], location + size) #Down right - + #Idea taken from flurick (on GitHub) if Global.draw_grid: for x in size.x: draw_line(Vector2(x, location.y), Vector2(x, size.y), Color.black, true) for y in size.y: draw_line(Vector2(location.x, y), Vector2(size.x, y), Color.black, true) - + #Draw rectangle to indicate the pixel currently being hovered on var mouse_pos := get_local_mouse_position() + location if point_in_rectangle(mouse_pos, location, location + size): @@ -299,7 +338,7 @@ func _draw() -> void: var custom_brush_size = Global.custom_left_brush_image.get_size() - Vector2.ONE var dst := rectangle_center(mouse_pos, custom_brush_size) draw_texture(Global.custom_left_brush_texture, dst) - + if Global.right_square_indicator_visible: match Global.current_right_brush_type: Global.BRUSH_TYPES.PIXEL: @@ -315,7 +354,7 @@ func generate_layer_panels() -> void: for child in Global.vbox_layer_container.get_children(): if child is PanelContainer: child.queue_free() - + current_layer_index = layers.size() - 1 if layers.size() == 1: Global.remove_layer_button.disabled = true @@ -323,7 +362,7 @@ func generate_layer_panels() -> void: else: Global.remove_layer_button.disabled = false Global.remove_layer_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND - + for i in range(layers.size() -1, -1, -1): var layer_container = load("res://Prefabs/LayerContainer.tscn").instance() layers[i][2] = "Layer %s" % i @@ -365,7 +404,7 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> var brush_index := -1 var custom_brush_image : Image var horizontal_mirror := false - var vertical_mirror := false + var vertical_mirror := false if current_mouse_button == "left_mouse": brush_size = Global.left_brush_size brush_type = Global.current_left_brush_type @@ -380,7 +419,7 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> custom_brush_image = Global.custom_right_brush_image horizontal_mirror = Global.right_horizontal_mirror vertical_mirror = Global.right_vertical_mirror - + var west_limit := location.x var east_limit := location.x + size.x var north_limit := location.y @@ -390,12 +429,12 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> east_limit = min(east_limit, Global.selection_rectangle.polygon[2].x) north_limit = max(north_limit, Global.selection_rectangle.polygon[0].y) south_limit = min(south_limit, Global.selection_rectangle.polygon[2].y) - + var start_pos_x var start_pos_y var end_pos_x var end_pos_y - + match(brush_type): Global.BRUSH_TYPES.PIXEL: start_pos_x = pos.x - (brush_size >> 1) @@ -423,7 +462,7 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> if layers[current_layer_index][0].get_pixel(mirror_x, mirror_y) != color: #don't draw the same pixel over and over layers[current_layer_index][0].set_pixel(mirror_x, mirror_y, color) sprite_changed_this_frame = true - + Global.BRUSH_TYPES.CUSTOM: var custom_brush_size := custom_brush_image.get_size() - Vector2.ONE pos = pos.floor() @@ -432,7 +471,7 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> #Rectangle with the same size as the brush, but at cursor's position #var pos_rect_position := rectangle_center(pos, custom_brush_size) var pos_rect := Rect2(dst, custom_brush_size + Vector2.ONE) - + #The selection rectangle #If there's no rectangle, the whole canvas is considered a selection var selection_rect := Rect2() @@ -443,7 +482,7 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> #If the size is 0, that means that the brush wasn't positioned inside the selection if pos_rect_clipped.size == Vector2.ZERO: return - + #Re-position src_rect and dst based on the clipped position var pos_difference := (pos_rect.position - pos_rect_clipped.position).abs() #Obviously, if pos_rect and pos_rect_clipped are the same, pos_difference is Vector2.ZERO @@ -454,11 +493,11 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> #... make sure pixels aren't being drawn outside the selection by adjusting src_rect's size src_rect.size.x = min(src_rect.size.x, selection_rect.size.x) src_rect.size.y = min(src_rect.size.y, selection_rect.size.y) - + #Handle mirroring var mirror_x := east_limit + west_limit - dst.x - 1 var mirror_y := south_limit + north_limit - dst.y - 1 - + if color.a > 0: #If it's the pencil layers[current_layer_index][0].blend_rect(custom_brush_image, src_rect, dst) if horizontal_mirror: @@ -467,14 +506,14 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> layers[current_layer_index][0].blend_rect(custom_brush_image, src_rect, Vector2(dst.x, mirror_y)) if horizontal_mirror && vertical_mirror: layers[current_layer_index][0].blend_rect(custom_brush_image, src_rect, Vector2(mirror_x, mirror_y)) - + else: #if it's transparent - if it's the eraser var custom_brush := Image.new() custom_brush.copy_from(Global.custom_brushes[brush_index]) custom_brush_size = custom_brush.get_size() custom_brush.resize(custom_brush_size.x * brush_size, custom_brush_size.y * brush_size, Image.INTERPOLATE_NEAREST) var custom_brush_blended = Global.blend_image_with_color(custom_brush, color, 1) - + layers[current_layer_index][0].blit_rect_mask(custom_brush_blended, custom_brush, src_rect, dst) if horizontal_mirror: layers[current_layer_index][0].blit_rect_mask(custom_brush_blended, custom_brush, src_rect, Vector2(mirror_x, dst.y)) @@ -482,7 +521,7 @@ func draw_pixel(pos : Vector2, color : Color, current_mouse_button : String) -> layers[current_layer_index][0].blit_rect_mask(custom_brush_blended, custom_brush, src_rect, Vector2(dst.x, mirror_y)) if horizontal_mirror && vertical_mirror: layers[current_layer_index][0].blit_rect_mask(custom_brush_blended, custom_brush, src_rect, Vector2(mirror_x, mirror_y)) - + layers[current_layer_index][0].lock() sprite_changed_this_frame = true @@ -529,10 +568,10 @@ func flood_fill(pos : Vector2, target_color : Color, replace_color : Color) -> v east_limit = min(east_limit, Global.selection_rectangle.polygon[2].x) north_limit = max(north_limit, Global.selection_rectangle.polygon[0].y) south_limit = min(south_limit, Global.selection_rectangle.polygon[2].y) - + if !point_in_rectangle(pos, Vector2(west_limit - 1, north_limit - 1), Vector2(east_limit, south_limit)): return - + var q = [pos] for n in q: var west : Vector2 = n @@ -566,4 +605,3 @@ func rectangle_center(pos : Vector2, size : Vector2) -> Vector2: func _on_Timer_timeout() -> void: Global.can_draw = true - \ No newline at end of file diff --git a/Scripts/Global.gd b/Scripts/Global.gd index ad0afdc87..c4314790e 100644 --- a/Scripts/Global.gd +++ b/Scripts/Global.gd @@ -1,5 +1,6 @@ extends Node +var undo_redo : UndoRedo var current_frame := 0 setget set_current_frame_label # warning-ignore:unused_class_variable var can_draw := false @@ -102,6 +103,7 @@ var custom_right_brush_texture := ImageTexture.new() func _ready() -> void: + undo_redo = UndoRedo.new() var root = get_tree().get_root() canvas = find_node_by_name(root, "Canvas") canvases.append(canvas) @@ -111,10 +113,10 @@ func _ready() -> void: split_screen_button = find_node_by_name(root, "SplitScreenButton") camera = find_node_by_name(canvas_parent, "Camera2D") camera2 = find_node_by_name(canvas_parent.get_parent().get_parent(), "Camera2D2") - + selection_rectangle = find_node_by_name(root, "SelectionRectangle") image_clipboard = Image.new() - + file_menu = find_node_by_name(root, "FileMenu") edit_menu = find_node_by_name(root, "EditMenu") view_menu = find_node_by_name(root, "ViewMenu") @@ -127,10 +129,10 @@ func _ready() -> void: right_brush_size_edit = find_node_by_name(root, "RightBrushSizeEdit") left_interpolate_slider = find_node_by_name(root, "LeftInterpolateFactor") right_interpolate_slider = find_node_by_name(root, "RightInterpolateFactor") - + left_brush_indicator = find_node_by_name(root, "LeftBrushIndicator") right_brush_indicator = find_node_by_name(root, "RightBrushIndicator") - + loop_animation_button = find_node_by_name(root, "LoopAnim") play_forward = find_node_by_name(root, "PlayForward") play_backwards = find_node_by_name(root, "PlayBackwards") @@ -147,18 +149,30 @@ func _ready() -> void: zoom_level_label = find_node_by_name(root, "ZoomLevel") current_frame_label = find_node_by_name(root, "CurrentFrame") -#Thanks to https://godotengine.org/qa/17524/how-to-find-an-instanced-scene-by-its-name +#Thanks to https://godotengine.org/qa/17524/how-to-find-an-instanced-scene-by-its-name func find_node_by_name(root, node_name) -> Node: - if root.get_name() == node_name: + if root.get_name() == node_name: return root for child in root.get_children(): if child.get_name() == node_name: return child var found = find_node_by_name(child, node_name) - if found: + if found: return found return null +func undo(canvas : Canvas, layer_index : int) -> void: + var action_name : String = undo_redo.get_current_action_name() + if action_name == "Draw" || action_name == "Rectangle Select": + canvas.update_texture(layer_index) + print("Undo: ", action_name) + +func redo(canvas : Canvas, layer_index : int) -> void: + var action_name : String = undo_redo.get_current_action_name() + if action_name == "Draw" || action_name == "Rectangle Select": + canvas.update_texture(layer_index) + print("Redo: ", action_name) + func change_frame() -> void: for c in canvases: c.visible = false @@ -174,7 +188,7 @@ func handle_layer_order_buttons() -> void: else: move_left_frame_button.disabled = false move_left_frame_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND - + if current_frame == canvases.size() - 1: move_right_frame_button.disabled = true move_right_frame_button.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN @@ -212,7 +226,7 @@ func update_left_custom_brush() -> void: custom_brush.resize(custom_brush_size.x * left_brush_size, custom_brush_size.y * left_brush_size, Image.INTERPOLATE_NEAREST) custom_left_brush_image = blend_image_with_color(custom_brush, left_color_picker.color, left_interpolate_slider.value) custom_left_brush_texture.create_from_image(custom_left_brush_image, 0) - + func update_right_custom_brush() -> void: if custom_right_brush_index > -1: var custom_brush := Image.new() diff --git a/Scripts/LayerContainer.gd b/Scripts/LayerContainer.gd index 175d1fa1a..88c81db65 100644 --- a/Scripts/LayerContainer.gd +++ b/Scripts/LayerContainer.gd @@ -26,14 +26,14 @@ func changed_selection() -> void: if Global.canvas.current_layer_index == child.i: child.currently_selected = true child.get_stylebox("panel").bg_color = Color("282532") - + if Global.canvas.current_layer_index < Global.canvas.layers.size() - 1: Global.move_up_layer_button.disabled = false Global.move_up_layer_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND else: Global.move_up_layer_button.disabled = true Global.move_up_layer_button.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN - + if Global.canvas.current_layer_index > 0: Global.move_down_layer_button.disabled = false Global.move_down_layer_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND diff --git a/Scripts/Main.gd b/Scripts/Main.gd index b49ea631c..6f9ba966b 100644 --- a/Scripts/Main.gd +++ b/Scripts/Main.gd @@ -19,7 +19,7 @@ func _ready() -> void: # Set a minimum window size to prevent UI elements from collapsing on each other. # This property is only available in 3.2alpha or later, so use `set()` to fail gracefully if it doesn't exist. OS.set("min_window_size", Vector2(1024, 600)) - + var file_menu_items := { "New..." : KEY_MASK_CTRL + KEY_N, "Open..." : KEY_MASK_CTRL + KEY_O, @@ -35,9 +35,9 @@ func _ready() -> void: "Crop Image" : 0, "Clear Selection" : 0, "Flip Horizontal": KEY_MASK_SHIFT + KEY_H, - "Flip Vertical": KEY_MASK_SHIFT + KEY_V - #"Undo" : KEY_MASK_CTRL + KEY_Z, - #"Redo" : KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Z, + "Flip Vertical": KEY_MASK_SHIFT + KEY_V, + "Undo" : KEY_MASK_CTRL + KEY_Z, + "Redo" : KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Z } var view_menu_items := { "Tile Mode" : KEY_MASK_CTRL + KEY_T, @@ -50,7 +50,7 @@ func _ready() -> void: var edit_menu : PopupMenu = Global.edit_menu.get_popup() view_menu = Global.view_menu.get_popup() var help_menu : PopupMenu = Global.help_menu.get_popup() - + var i = 0 for item in file_menu_items.keys(): file_menu.add_item(item, i, file_menu_items[item]) @@ -67,12 +67,12 @@ func _ready() -> void: for item in help_menu_items.keys(): help_menu.add_item(item, i, help_menu_items[item]) i += 1 - + file_menu.connect("id_pressed", self, "file_menu_id_pressed") edit_menu.connect("id_pressed", self, "edit_menu_id_pressed") view_menu.connect("id_pressed", self, "view_menu_id_pressed") help_menu.connect("id_pressed", self, "help_menu_id_pressed") - + var root = get_tree().get_root() #Node, left mouse shortcut, right mouse shortcut tools.append([Global.find_node_by_name(root, "Pencil"), "left_pencil_tool", "right_pencil_tool"]) @@ -81,15 +81,15 @@ func _ready() -> void: tools.append([Global.find_node_by_name(root, "PaintAllPixelsSameColor"), "left_paint_all_tool", "right_paint_all_tool"]) tools.append([Global.find_node_by_name(root, "LightenDarken"), "left_lightdark_tool", "right_lightdark_tool"]) tools.append([Global.find_node_by_name(root, "RectSelect"), "left_rectangle_select_tool", "right_rectangle_select_tool"]) - + for t in tools: t[0].connect("pressed", self, "_on_Tool_pressed", [t[0]]) - + #Options for Import import_as_new_frame = CheckBox.new() import_as_new_frame.text = "Import as new frame?" $ImportSprites.get_vbox().add_child(import_as_new_frame) - + #Options for Export export_all_frames = CheckBox.new() export_all_frames.text = "Export all frames?" @@ -100,7 +100,7 @@ func _ready() -> void: $ExportSprites.get_vbox().add_child(export_all_frames) $ExportSprites.get_vbox().add_child(export_as_single_file) $ExportSprites.get_vbox().add_child(export_vertical_spritesheet) - + func _input(event): for t in tools: #Handle tool shortcuts @@ -156,16 +156,16 @@ func edit_menu_id_pressed(id : int) -> void: while(i < Global.canvas.layers.size() - 1 && Global.canvas.layers[i][0].get_used_rect() == Rect2(0, 0, 0, 0)): i += 1 used_rect = Global.canvas.layers[i][0].get_used_rect() - + #Merge all layers with content for j in range(Global.canvas.layers.size() - 1, i, -1): if Global.canvas.layers[j][0].get_used_rect() != Rect2(0, 0, 0, 0): used_rect = used_rect.merge(Global.canvas.layers[j][0].get_used_rect()) - + #If no layer has any content, just return if used_rect == Rect2(0, 0, 0, 0): return - + #Loop through all the layers to crop them for j in range(Global.canvas.layers.size() - 1, -1, -1): var sprite := Image.new() @@ -173,7 +173,7 @@ func edit_menu_id_pressed(id : int) -> void: Global.canvas.layers[j][0] = sprite Global.canvas.layers[j][0].lock() Global.canvas.update_texture(j) - + var width = Global.canvas.layers[0][0].get_width() var height = Global.canvas.layers[0][0].get_height() Global.canvas.size = Vector2(width, height).floor() @@ -196,6 +196,10 @@ func edit_menu_id_pressed(id : int) -> void: canvas.layers[canvas.current_layer_index][0].flip_y() canvas.layers[canvas.current_layer_index][0].lock() canvas.update_texture(canvas.current_layer_index) + 5: #Undo + Global.undo_redo.undo() + 6: #Redo + Global.undo_redo.redo() func view_menu_id_pressed(id : int) -> void: match id: @@ -213,13 +217,13 @@ func help_menu_id_pressed(id : int) -> void: Global.can_draw = false func _on_CreateNewImage_confirmed() -> void: - var width := float($CreateNewImage/VBoxContainer/WidthCont/WidthValue.value) - var height := float($CreateNewImage/VBoxContainer/HeightCont/HeightValue.value) + var width = $CreateNewImage/VBoxContainer/WidthCont/WidthValue.value + var height = $CreateNewImage/VBoxContainer/HeightCont/HeightValue.value var fill_color : Color = $CreateNewImage/VBoxContainer/FillColor/FillColor.color clear_canvases() Global.canvas = load("res://Prefabs/Canvas.tscn").instance() Global.canvas.size = Vector2(width, height).floor() - + Global.canvas_parent.add_child(Global.canvas) Global.canvases.append(Global.canvas) Global.current_frame = 0 @@ -246,10 +250,10 @@ func _on_OpenSprite_file_selected(path) -> void: Global.canvas = canvas var width := file.get_16() var height := file.get_16() - + var layer := 0 var layer_line := file.get_line() - + while layer_line == "-": var buffer := file.get_buffer(width * height * 4) var image := Image.new() @@ -260,14 +264,14 @@ func _on_OpenSprite_file_selected(path) -> void: canvas.layers.append([image, tex, "Layer %s" % layer, true]) layer_line = file.get_line() layer += 1 - + canvas.size = Vector2(width, height) Global.canvases.append(canvas) canvas.frame = frame Global.canvas_parent.add_child(canvas) frame_line = file.get_line() frame += 1 - + Global.current_frame = frame - 1 #Load tool options Global.left_color_picker.color = file.get_var() @@ -282,11 +286,11 @@ func _on_OpenSprite_file_selected(path) -> void: Global.left_color_picker.get_picker().add_preset(color) for color in right_palette: Global.right_color_picker.get_picker().add_preset(color) - + #Load custom brushes Global.custom_brushes.clear() Global.remove_brush_buttons() - + var brush_line := file.get_line() while brush_line == "/": var b_width := file.get_16() @@ -297,7 +301,7 @@ func _on_OpenSprite_file_selected(path) -> void: Global.custom_brushes.append(image) Global.create_brush_button(image) brush_line = file.get_line() - + file.close() func _on_SaveSprite_file_selected(path) -> void: @@ -315,7 +319,7 @@ func _on_SaveSprite_file_selected(path) -> void: file.store_buffer(layer[0].get_data()) file.store_line("END_LAYERS") file.store_line("END_FRAMES") - + #Save tool options var left_color := Global.left_color_picker.color var right_color := Global.right_color_picker.color @@ -341,7 +345,7 @@ func _on_SaveSprite_file_selected(path) -> void: func _on_ImportSprites_files_selected(paths) -> void: if !import_as_new_frame.pressed: #If we're not adding a new frame, delete the previous clear_canvases() - + #Find the biggest image and let it handle the camera zoom options var max_size : Vector2 var biggest_canvas : Canvas @@ -369,10 +373,10 @@ func _on_ImportSprites_files_selected(paths) -> void: else: if canvas.size > max_size: biggest_canvas = canvas - + else: OS.alert("Can't load file") - + i += 1 Global.current_frame = i - 1 Global.canvas = Global.canvases[Global.canvases.size() - 1] @@ -385,7 +389,7 @@ func _on_ImportSprites_files_selected(paths) -> void: else: Global.remove_frame_button.disabled = true Global.remove_frame_button.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN - + func clear_canvases() -> void: for child in Global.vbox_layer_container.get_children(): if child is PanelContainer: @@ -454,7 +458,7 @@ func save_spritesheet() -> void: dst += Vector2(0, canvas.size.y) else: dst += Vector2(canvas.size.x, 0) - + var err = whole_image.save_png(current_export_path) if err != OK: OS.alert("Can't save file") @@ -468,7 +472,7 @@ func _on_ViewportContainer_mouse_entered() -> void: func _on_ViewportContainer_mouse_exited() -> void: Global.has_focus = false - + func _can_draw_true() -> void: Global.can_draw = true func _can_draw_false() -> void: @@ -486,8 +490,8 @@ func _on_Tool_pressed(tool_pressed : BaseButton, mouse_press := true, key_for_le tool_pressed.add_child(Global.right_indicator) func _on_ScaleImage_confirmed() -> void: - var width = float($ScaleImage/VBoxContainer/WidthCont/WidthValue.value) - var height = float($ScaleImage/VBoxContainer/HeightCont/HeightValue.value) + var width = $ScaleImage/VBoxContainer/WidthCont/WidthValue.value + var height = $ScaleImage/VBoxContainer/HeightCont/HeightValue.value for i in range(Global.canvas.layers.size() - 1, -1, -1): var sprite = Image.new() sprite = Global.canvas.layers[i][1].get_data() @@ -495,7 +499,7 @@ func _on_ScaleImage_confirmed() -> void: Global.canvas.layers[i][0] = sprite Global.canvas.layers[i][0].lock() Global.canvas.update_texture(i) - + Global.canvas.size = Vector2(width, height).floor() Global.canvas.camera_zoom() @@ -526,15 +530,15 @@ func _on_MoveDownLayer_pressed() -> void: func change_layer_order(rate : int) -> void: var change = Global.canvas.current_layer_index + rate - + var temp = Global.canvas.layers[Global.canvas.current_layer_index] Global.canvas.layers[Global.canvas.current_layer_index] = Global.canvas.layers[change] Global.canvas.layers[change] = temp - + Global.canvas.generate_layer_panels() Global.canvas.current_layer_index = change Global.canvas.get_layer_container(Global.canvas.current_layer_index).changed_selection() - + func _on_CloneLayer_pressed() -> void: add_layer(false) @@ -570,7 +574,7 @@ func _on_AddFrame_pressed() -> void: Global.canvases.append(canvas) Global.current_frame = Global.canvases.size() - 1 Global.canvas = canvas - + Global.canvas_parent.add_child(canvas) Global.remove_frame_button.disabled = false Global.remove_frame_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND @@ -614,7 +618,7 @@ func _on_CloneFrame_pressed() -> void: Global.canvases.append(canvas) Global.current_frame = Global.canvases.size() - 1 Global.canvas = canvas - + Global.canvas_parent.add_child(canvas) Global.remove_frame_button.disabled = false Global.remove_frame_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND @@ -630,21 +634,21 @@ func _on_MoveFrameRight_pressed() -> void: func change_frame_order(rate : int) -> void: var frame_button = Global.frame_container.get_node("Frame_%s" % Global.current_frame) var change = Global.current_frame + rate - + var temp = Global.canvases[Global.current_frame] Global.canvases[Global.current_frame] = Global.canvases[change] Global.canvases[change] = temp - + #Clear frame button names first, to avoid duplicates like two Frame_0s for canvas in Global.canvases: canvas.frame_button.name = "frame" - + for canvas in Global.canvases: canvas.frame = Global.canvases.find(canvas) canvas.frame_button.name = "Frame_%s" % canvas.frame canvas.frame_button.get_node("FrameButton").frame = canvas.frame canvas.frame_button.get_node("FrameID").text = str(canvas.frame + 1) - + Global.current_frame = change Global.frame_container.move_child(frame_button, Global.current_frame) Global.canvas_parent.move_child(Global.canvas, Global.current_frame) @@ -669,7 +673,7 @@ func _on_LoopAnim_pressed() -> void: func _on_PlayForward_toggled(button_pressed) -> void: Global.play_backwards.pressed = false Global.play_backwards.text = "Play Backwards" - + if button_pressed: Global.play_forward.text = "Stop" $AnimationTimer.wait_time = 1 / fps @@ -682,7 +686,7 @@ func _on_PlayForward_toggled(button_pressed) -> void: func _on_PlayBackwards_toggled(button_pressed) -> void: Global.play_forward.pressed = false Global.play_forward.text = "Play Forward" - + if button_pressed: Global.play_backwards.text = "Stop" $AnimationTimer.wait_time = 1 / fps @@ -709,7 +713,7 @@ func _on_AnimationTimer_timeout() -> void: 2: #Ping pong loop animation_forward = false _on_AnimationTimer_timeout() - + else: if Global.current_frame > 0: Global.current_frame -= 1 @@ -726,7 +730,7 @@ func _on_AnimationTimer_timeout() -> void: 2: #Ping pong loop animation_forward = true _on_AnimationTimer_timeout() - + Global.change_frame() func _on_FPSValue_value_changed(value) -> void: diff --git a/Scripts/SelectionRectangle.gd b/Scripts/SelectionRectangle.gd index 27386214f..17823d20c 100644 --- a/Scripts/SelectionRectangle.gd +++ b/Scripts/SelectionRectangle.gd @@ -12,7 +12,6 @@ var orig_colors := [] func _ready() -> void: img = Image.new() - #img.create(Global.canvas.size.x, Global.canvas.size.y, false, Image.FORMAT_RGBA8) img.create(1, 1, false, Image.FORMAT_RGBA8) img.lock() tex = ImageTexture.new() @@ -24,8 +23,9 @@ func _process(delta) -> void: var mouse_pos_floored := mouse_pos.floor() var start_pos := polygon[0] var end_pos := polygon[2] - var layer : Image = Global.canvas.layers[Global.canvas.current_layer_index][0] - + var current_layer_index := Global.canvas.current_layer_index + var layer : Image = Global.canvas.layers[current_layer_index][0] + if point_in_rectangle(mouse_pos, polygon[0], polygon[2]) && Global.selected_pixels.size() > 0 && (Global.current_left_tool == "RectSelect" || Global.current_right_tool == "RectSelect"): get_parent().get_parent().mouse_default_cursor_shape = Input.CURSOR_MOVE if (Global.current_left_tool == "RectSelect" && Input.is_action_just_pressed("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_just_pressed("right_mouse")): @@ -46,25 +46,25 @@ func _process(delta) -> void: for i in range(Global.selected_pixels.size()): var curr_px = Global.selected_pixels[i] if point_in_rectangle(curr_px, Global.canvas.location - Vector2.ONE, Global.canvas.size): - orig_colors.append(layer.get_pixelv(curr_px)) + orig_colors.append(layer.get_pixelv(curr_px)) #Color of pixel var px = curr_px - Global.selected_pixels[0] img.set_pixelv(px, orig_colors[i]) layer.set_pixelv(curr_px, Color(0, 0, 0, 0)) - else: + else: #If part of selection is outside canvas orig_colors.append(Color(0, 0, 0, 0)) Global.canvas.update_texture(Global.canvas.current_layer_index) tex.create_from_image(img, 0) update() else: get_parent().get_parent().mouse_default_cursor_shape = Input.CURSOR_CROSS - + if is_dragging: if (Global.current_left_tool == "RectSelect" && Input.is_action_pressed("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_pressed("right_mouse")): #Drag #if orig_x + mouse_pos_floored.x >= Global.canvas.location.x && diff_x + mouse_pos_floored.x <= Global.canvas.size.x: start_pos.x = orig_x + mouse_pos_floored.x end_pos.x = diff_x + mouse_pos_floored.x - + #if orig_y + mouse_pos_floored.y >= Global.canvas.location.y && diff_y + mouse_pos_floored.y <= Global.canvas.size.y: start_pos.y = orig_y + mouse_pos_floored.y end_pos.y = diff_y + mouse_pos_floored.y @@ -72,7 +72,7 @@ func _process(delta) -> void: polygon[1] = Vector2(end_pos.x, start_pos.y) polygon[2] = end_pos polygon[3] = Vector2(start_pos.x, end_pos.y) - + if (Global.current_left_tool == "RectSelect" && Input.is_action_just_released("left_mouse")) || (Global.current_right_tool == "RectSelect" && Input.is_action_just_released("right_mouse")): #Release Drag is_dragging = false @@ -86,13 +86,15 @@ func _process(delta) -> void: img.fill(Color(0, 0, 0, 0)) tex.create_from_image(img, 0) update() - + orig_colors.clear() Global.selected_pixels.clear() for xx in range(start_pos.x, end_pos.x): for yy in range(start_pos.y, end_pos.y): Global.selected_pixels.append(Vector2(xx, yy)) - + + Global.canvas.handle_redo("Rectangle Select") #Redo + #Handle copy if Input.is_action_just_pressed("copy") && Global.selected_pixels.size() > 0: Global.image_clipboard = layer.get_rect(Rect2(polygon[0], polygon[2] - polygon[0])) @@ -101,15 +103,16 @@ func _process(delta) -> void: brush_img = layer.get_rect(Rect2(polygon[0], polygon[2] - polygon[0])) brush_img = brush_img.get_rect(brush_img.get_used_rect()) #save only the visible pixels Global.custom_brushes.append(brush_img) - + Global.create_brush_button(brush_img) - + #Handle paste - #if Input.is_action_just_pressed("paste") && Global.selected_pixels.size() > 0 && !is_dragging: if Input.is_action_just_pressed("paste") && Global.selected_pixels.size() > 0 && Global.image_clipboard.get_size() > Vector2.ZERO: + Global.canvas.handle_undo("Draw") layer.blend_rect(Global.image_clipboard, Rect2(Vector2.ZERO, polygon[2]-polygon[0]), polygon[0]) layer.lock() - Global.canvas.update_texture(Global.canvas.current_layer_index) + Global.canvas.handle_redo("Draw") + #Global.canvas.update_texture(Global.canvas.current_layer_index) func _draw() -> void: if img.get_size() == polygon[2] - polygon[0]: @@ -117,4 +120,3 @@ func _draw() -> void: func point_in_rectangle(p : Vector2, coord1 : Vector2, coord2 : Vector2) -> bool: return p.x > coord1.x && p.y > coord1.y && p.x < coord2.x && p.y < coord2.y - \ No newline at end of file diff --git a/export_presets.cfg b/export_presets.cfg index 5f6ed0d0c..80bac5dfb 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -7,7 +7,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="" -export_path="C:/Users/Overloaded/Dropbox/Orama Interactive/Projects/[Programa Labs]/Pixelorama/Stable/Pixelorama.exe" +export_path="C:/Users/Overloaded/Dropbox/Orama Interactive/Projects/[Programa Labs]/Pixelorama/Full of bugs/Pixelorama.exe" patch_list=PoolStringArray( ) script_export_mode=1 script_encryption_key="" diff --git a/project.godot b/project.godot index 115ce82e6..9e00cf6a3 100644 --- a/project.godot +++ b/project.godot @@ -131,16 +131,6 @@ right_lightdark_tool={ "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":85,"unicode":0,"echo":false,"script":null) ] } -flip_h={ -"deadzone": 0.5, -"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":false,"meta":false,"command":false,"pressed":false,"scancode":72,"unicode":0,"echo":false,"script":null) - ] -} -flip_v={ -"deadzone": 0.5, -"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":false,"meta":false,"command":false,"pressed":false,"scancode":86,"unicode":0,"echo":false,"script":null) - ] -} [rendering]