From de850ce8a11299f263fb867de7ecc3c792783fc3 Mon Sep 17 00:00:00 2001 From: Manolis Papadeas <35376950+OverloadedOrama@users.noreply.github.com> Date: Sat, 12 Jun 2021 01:06:13 +0300 Subject: [PATCH] Ability to select multiple cels and edit them all at once A rather significant change, so, while I have tested it, it might still be buggy. Implements a part of #306. --- src/Classes/ImageEffect.gd | 45 ++++++----- src/Classes/Project.gd | 33 +++++--- src/Tools/BaseTool.gd | 9 +++ src/Tools/Bucket.gd | 114 ++++++++++++++------------- src/Tools/Draw.gd | 36 +++++---- src/Tools/Eraser.gd | 5 +- src/Tools/Move.gd | 57 ++++++++++++-- src/Tools/Pencil.gd | 14 ++-- src/UI/Canvas/Canvas.gd | 18 ++++- src/UI/Timeline/AnimationTimeline.gd | 12 ++- src/UI/Timeline/CelButton.gd | 48 ++++++++++- src/UI/Timeline/FrameButton.gd | 30 ++++++- src/UI/Timeline/LayerButton.gd | 28 ++++++- src/UI/Timeline/LayerButton.tscn | 1 + 14 files changed, 322 insertions(+), 128 deletions(-) diff --git a/src/Classes/ImageEffect.gd b/src/Classes/ImageEffect.gd index b88d7d0cc..780b4a8bb 100644 --- a/src/Classes/ImageEffect.gd +++ b/src/Classes/ImageEffect.gd @@ -43,41 +43,50 @@ func _about_to_show() -> void: func _confirmed() -> void: + var project := Global.current_project if affect == CEL: - if !Global.current_project.layers[Global.current_project.current_layer].can_layer_get_drawn(): # No changes if the layer is locked or invisible + if !project.layers[project.current_layer].can_layer_get_drawn(): # No changes if the layer is locked or invisible return - Global.canvas.handle_undo("Draw") - commit_action(current_cel) - Global.canvas.handle_redo("Draw") + if project.selected_cels.size() == 1: + Global.canvas.handle_undo("Draw") + commit_action(current_cel) + Global.canvas.handle_redo("Draw") + else: + Global.canvas.handle_undo("Draw", project, -1, -1) + for cel_index in project.selected_cels: + var cel : Cel = project.frames[cel_index[0]].cels[cel_index[1]] + var cel_image : Image = cel.image + commit_action(cel_image) + Global.canvas.handle_redo("Draw", project, -1, -1) elif affect == FRAME: - Global.canvas.handle_undo("Draw", Global.current_project, -1) + Global.canvas.handle_undo("Draw", project, -1) var i := 0 - for cel in Global.current_project.frames[Global.current_project.current_frame].cels: - if Global.current_project.layers[i].can_layer_get_drawn(): + for cel in project.frames[project.current_frame].cels: + if project.layers[i].can_layer_get_drawn(): commit_action(cel.image) i += 1 - Global.canvas.handle_redo("Draw", Global.current_project, -1) + Global.canvas.handle_redo("Draw", project, -1) elif affect == ALL_FRAMES: - Global.canvas.handle_undo("Draw", Global.current_project, -1, -1) - for frame in Global.current_project.frames: + Global.canvas.handle_undo("Draw", project, -1, -1) + for frame in project.frames: var i := 0 for cel in frame.cels: - if Global.current_project.layers[i].can_layer_get_drawn(): + if project.layers[i].can_layer_get_drawn(): commit_action(cel.image) i += 1 - Global.canvas.handle_redo("Draw", Global.current_project, -1, -1) + Global.canvas.handle_redo("Draw", project, -1, -1) elif affect == ALL_PROJECTS: - for project in Global.projects: - Global.canvas.handle_undo("Draw", project, -1, -1) - for frame in project.frames: + for _project in Global.projects: + Global.canvas.handle_undo("Draw", _project, -1, -1) + for frame in _project.frames: var i := 0 for cel in frame.cels: - if project.layers[i].can_layer_get_drawn(): - commit_action(cel.image, project) + if _project.layers[i].can_layer_get_drawn(): + commit_action(cel.image, _project) i += 1 - Global.canvas.handle_redo("Draw", project, -1, -1) + Global.canvas.handle_redo("Draw", _project, -1, -1) func commit_action(_cel : Image, _project : Project = Global.current_project) -> void: diff --git a/src/Classes/Project.gd b/src/Classes/Project.gd index 076416b0c..8022ee6c6 100644 --- a/src/Classes/Project.gd +++ b/src/Classes/Project.gd @@ -12,6 +12,8 @@ var frames := [] setget frames_changed # Array of Frames (that contain Cels) var layers := [] setget layers_changed # Array of Layers var current_frame := 0 setget frame_changed var current_layer := 0 setget layer_changed +var selected_cels := [[0, 0]] # Array of Arrays of 2 integers (frame & layer) + var animation_tags := [] setget animation_tags_changed # Array of AnimationTags var guides := [] # Array of Guides var brushes := [] # Array of Images @@ -386,6 +388,7 @@ func size_changed(value : Vector2) -> void: func frames_changed(value : Array) -> void: frames = value + selected_cels.clear() remove_cel_buttons() for frame_id in Global.frame_ids.get_children(): @@ -419,6 +422,8 @@ func layers_changed(value : Array) -> void: Global.layers_changed_skip = false return + selected_cels.clear() + for container in Global.layers_container.get_children(): container.queue_free() @@ -471,11 +476,16 @@ func frame_changed(value : int) -> void: if i < layer.frame_container.get_child_count(): layer.frame_container.get_child(i).pressed = false + if selected_cels.empty(): + selected_cels.append([current_frame, current_layer]) # Select the new frame - if current_frame < Global.frame_ids.get_child_count(): - Global.frame_ids.get_child(current_frame).add_color_override("font_color", Global.control.theme.get_color("Selected Color", "Label")) - if layers and current_frame < layers[current_layer].frame_container.get_child_count(): - layers[current_layer].frame_container.get_child(current_frame).pressed = true + for cel in selected_cels: + var _current_frame : int = cel[0] + var _current_layer : int = cel[1] + if _current_frame < Global.frame_ids.get_child_count(): + Global.frame_ids.get_child(_current_frame).add_color_override("font_color", Global.control.theme.get_color("Selected Color", "Label")) + if _current_frame < layers[_current_layer].frame_container.get_child_count(): + layers[_current_layer].frame_container.get_child(_current_frame).pressed = true Global.disable_button(Global.remove_frame_button, frames.size() == 1) Global.disable_button(Global.move_left_frame_button, frames.size() == 1 or current_frame == 0) @@ -493,17 +503,18 @@ func layer_changed(value : int) -> void: Global.canvas.selection.transform_content_confirm() current_layer = value - for container in Global.layers_container.get_children(): - container.pressed = false - - if current_layer < Global.layers_container.get_child_count(): - var layer_button = Global.layers_container.get_child(Global.layers_container.get_child_count() - 1 - current_layer) - layer_button.pressed = true - toggle_layer_buttons_current_layer() yield(Global.get_tree().create_timer(0.01), "timeout") self.current_frame = current_frame # Call frame_changed to update UI + for layer_button in Global.layers_container.get_children(): + layer_button.pressed = false + + for cel in selected_cels: + var _current_layer : int = cel[1] + if _current_layer < Global.layers_container.get_child_count(): + var layer_button = Global.layers_container.get_child(Global.layers_container.get_child_count() - 1 - _current_layer) + layer_button.pressed = true func toggle_layer_buttons_layers() -> void: diff --git a/src/Tools/BaseTool.gd b/src/Tools/BaseTool.gd index b368812a5..17d009763 100644 --- a/src/Tools/BaseTool.gd +++ b/src/Tools/BaseTool.gd @@ -94,6 +94,15 @@ func _get_draw_image() -> Image: return project.frames[project.current_frame].cels[project.current_layer].image +func _get_selected_draw_images() -> Array: # Array of Images + var images := [] + var project : Project = Global.current_project + for cel_index in project.selected_cels: + var cel : Cel = project.frames[cel_index[0]].cels[cel_index[1]] + images.append(cel.image) + return images + + func _flip_rect(rect : Rect2, size : Vector2, horizontal : bool, vertical : bool) -> Rect2: var result := rect if horizontal: diff --git a/src/Tools/Bucket.gd b/src/Tools/Bucket.gd index 0b5059d23..c4e79d706 100644 --- a/src/Tools/Bucket.gd +++ b/src/Tools/Bucket.gd @@ -117,21 +117,22 @@ func draw_end(_position : Vector2) -> void: func fill_in_color(position : Vector2) -> void: var project : Project = Global.current_project - var image := _get_draw_image() - var color := image.get_pixelv(position) - if _fill_with == 0 or _pattern == null: - if tool_slot.color.is_equal_approx(color): - return + var color : Color = _get_draw_image().get_pixelv(position) + var images := _get_selected_draw_images() + for image in images: + if _fill_with == 0 or _pattern == null: + if tool_slot.color.is_equal_approx(color): + return - image.lock() + image.lock() - for x in Global.current_project.size.x: - for y in Global.current_project.size.y: - var pos := Vector2(x, y) - if project.has_selection and not project.can_pixel_get_drawn(pos): - continue - if image.get_pixelv(pos).is_equal_approx(color): - _set_pixel(image, x, y, tool_slot.color) + for x in Global.current_project.size.x: + for y in Global.current_project.size.y: + var pos := Vector2(x, y) + if project.has_selection and not project.can_pixel_get_drawn(pos): + continue + if image.get_pixelv(pos).is_equal_approx(color): + _set_pixel(image, x, y, tool_slot.color) func fill_in_area(position : Vector2) -> void: @@ -157,35 +158,36 @@ func fill_in_area(position : Vector2) -> void: func _flood_fill(position : Vector2) -> void: var project : Project = Global.current_project - var image := _get_draw_image() - var color := image.get_pixelv(position) - if _fill_with == 0 or _pattern == null: - if tool_slot.color.is_equal_approx(color): - return + var images := _get_selected_draw_images() + for image in images: + var color : Color = image.get_pixelv(position) + if _fill_with == 0 or _pattern == null: + if tool_slot.color.is_equal_approx(color): + return - image.lock() - var processed := BitMap.new() - processed.create(image.get_size()) - var q = [position] - for n in q: - if processed.get_bit(n): - continue - var west : Vector2 = n - var east : Vector2 = n - while west.x >= 0 && image.get_pixelv(west).is_equal_approx(color): - west += Vector2.LEFT - while east.x < project.size.x && image.get_pixelv(east).is_equal_approx(color): - east += Vector2.RIGHT - for px in range(west.x + 1, east.x): - var p := Vector2(px, n.y) - _set_pixel(image, p.x, p.y, tool_slot.color) - processed.set_bit(p, true) - var north := p + Vector2.UP - var south := p + Vector2.DOWN - if north.y >= 0 && image.get_pixelv(north).is_equal_approx(color): - q.append(north) - if south.y < project.size.y && image.get_pixelv(south).is_equal_approx(color): - q.append(south) + image.lock() + var processed := BitMap.new() + processed.create(image.get_size()) + var q = [position] + for n in q: + if processed.get_bit(n): + continue + var west : Vector2 = n + var east : Vector2 = n + while west.x >= 0 && image.get_pixelv(west).is_equal_approx(color): + west += Vector2.LEFT + while east.x < project.size.x && image.get_pixelv(east).is_equal_approx(color): + east += Vector2.RIGHT + for px in range(west.x + 1, east.x): + var p := Vector2(px, n.y) + _set_pixel(image, p.x, p.y, tool_slot.color) + processed.set_bit(p, true) + var north := p + Vector2.UP + var south := p + Vector2.DOWN + if north.y >= 0 && image.get_pixelv(north).is_equal_approx(color): + q.append(north) + if south.y < project.size.y && image.get_pixelv(south).is_equal_approx(color): + q.append(south) func _set_pixel(image : Image, x : int, y : int, color : Color) -> void: @@ -205,24 +207,30 @@ func _set_pixel(image : Image, x : int, y : int, color : Color) -> void: func commit_undo(action : String, undo_data : Dictionary) -> void: - var redo_data = _get_undo_data() + var redo_data := _get_undo_data() var project : Project = Global.current_project - var image : Image = project.frames[project.current_frame].cels[project.current_layer].image + var frame := -1 + var layer := -1 + if Global.animation_timer.is_stopped() and project.selected_cels.size() == 1: + frame = project.current_frame + layer = project.current_layer project.undos += 1 project.undo_redo.create_action(action) - project.undo_redo.add_do_property(image, "data", redo_data["image_data"]) - project.undo_redo.add_undo_property(image, "data", undo_data["image_data"]) - project.undo_redo.add_do_method(Global, "redo", project.current_frame, project.current_layer) - project.undo_redo.add_undo_method(Global, "undo", project.current_frame, project.current_layer) + for image in redo_data: + project.undo_redo.add_do_property(image, "data", redo_data[image]) + for image in undo_data: + project.undo_redo.add_undo_property(image, "data", undo_data[image]) + project.undo_redo.add_do_method(Global, "redo", frame, layer) + project.undo_redo.add_undo_method(Global, "undo", frame, layer) project.undo_redo.commit_action() func _get_undo_data() -> Dictionary: - var data = {} - var project : Project = Global.current_project - var image : Image = project.frames[project.current_frame].cels[project.current_layer].image - image.unlock() - data["image_data"] = image.data - image.lock() + var data := {} + var images := _get_selected_draw_images() + for image in images: + image.unlock() + data[image] = image.data + image.lock() return data diff --git a/src/Tools/Draw.gd b/src/Tools/Draw.gd index e8e7af03e..b5b4dea77 100644 --- a/src/Tools/Draw.gd +++ b/src/Tools/Draw.gd @@ -130,7 +130,7 @@ func update_mirror_brush() -> void: func update_mask(can_skip := true) -> void: if can_skip and Global.pressure_sensitivity_mode == Global.PressureSensitivity.NONE: return - var size := _get_draw_image().get_size() + var size : Vector2 = Global.current_project.size # Faster than zeroing PoolByteArray directly. See: https://github.com/Orama-Interactive/Pixelorama/pull/439 var nulled_array := [] nulled_array.resize(size.x * size.y) @@ -155,11 +155,11 @@ func prepare_undo() -> void: func commit_undo(action : String) -> void: - var redo_data = _get_undo_data() + var redo_data := _get_undo_data() var project : Project = Global.current_project var frame := -1 var layer := -1 - if Global.animation_timer.is_stopped(): + if Global.animation_timer.is_stopped() and project.selected_cels.size() == 1: frame = project.current_frame layer = project.current_layer @@ -347,14 +347,15 @@ func _set_pixel(position : Vector2) -> void: if !project.can_pixel_get_drawn(position): return - var image := _get_draw_image() - var i := int(position.x + position.y * image.get_size().x) - if _mask.size() >= i + 1: - if _mask[i] < Tools.pen_pressure: - _mask[i] = Tools.pen_pressure + var images := _get_selected_draw_images() + for image in images: + var i := int(position.x + position.y * image.get_size().x) + if _mask.size() >= i + 1: + if _mask[i] < Tools.pen_pressure: + _mask[i] = Tools.pen_pressure + _drawer.set_pixel(image, position, tool_slot.color) + else: _drawer.set_pixel(image, position, tool_slot.color) - else: - _drawer.set_pixel(image, position, tool_slot.color) func _draw_brush_image(_image : Image, _src_rect: Rect2, _dst: Vector2) -> void: @@ -515,13 +516,18 @@ func _line_angle_constraint(start : Vector2, end : Vector2) -> Dictionary: func _get_undo_data() -> Dictionary: - var data = {} + var data := {} var project : Project = Global.current_project - var frames := project.frames + var cels := [] # Array of Cels if Global.animation_timer.is_stopped(): - frames = [project.frames[project.current_frame]] - for frame in frames: - var image : Image = frame.cels[project.current_layer].image + for cel_index in project.selected_cels: + cels.append(project.frames[cel_index[0]].cels[cel_index[1]]) + else: + for frame in project.frames: + var cel : Cel = frame.cels[project.current_layer] + cels.append(cel) + for cel in cels: + var image : Image = cel.image image.unlock() data[image] = image.data image.lock() diff --git a/src/Tools/Eraser.gd b/src/Tools/Eraser.gd index 847a4af48..eb20df543 100644 --- a/src/Tools/Eraser.gd +++ b/src/Tools/Eraser.gd @@ -73,4 +73,7 @@ func _draw_brush_image(_image : Image, src_rect: Rect2, dst: Vector2) -> void: var size := _image.get_size() if _clear_image.get_size() != size: _clear_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST) - _get_draw_image().blit_rect_mask(_clear_image, _image, src_rect, dst) + + var images := _get_selected_draw_images() + for draw_image in images: + draw_image.blit_rect_mask(_clear_image, _image, src_rect, dst) diff --git a/src/Tools/Move.gd b/src/Tools/Move.gd index 0d6dd4d6c..0b805ef9f 100644 --- a/src/Tools/Move.gd +++ b/src/Tools/Move.gd @@ -1,6 +1,7 @@ extends BaseTool +var _undo_data := {} var _start_pos : Vector2 var _offset : Vector2 @@ -29,6 +30,7 @@ func _input(event : InputEvent) -> void: func draw_start(position : Vector2) -> void: _start_pos = position _offset = position + _undo_data = _get_undo_data() if Global.current_project.has_selection: selection_node.transform_content_start() _content_transformation_check = selection_node.is_moving_content @@ -72,19 +74,60 @@ func draw_end(position : Vector2) -> void: var pixel_diff : Vector2 = position - _start_pos var project : Project = Global.current_project - var image : Image = _get_draw_image() if project.has_selection: selection_node.move_borders_end() else: Global.canvas.move_preview_location = Vector2.ZERO - var image_copy := Image.new() - image_copy.copy_from(image) - Global.canvas.handle_undo("Draw") - image.fill(Color(0, 0, 0, 0)) - image.blit_rect(image_copy, Rect2(Vector2.ZERO, project.size), pixel_diff) + var images := _get_selected_draw_images() + for image in images: + var image_copy := Image.new() + image_copy.copy_from(image) + image.fill(Color(0, 0, 0, 0)) + image.blit_rect(image_copy, Rect2(Vector2.ZERO, project.size), pixel_diff) - Global.canvas.handle_redo("Draw") + commit_undo("Draw") _start_pos = Vector2.INF _snap_to_grid = false + + +func commit_undo(action : String) -> void: + var redo_data := _get_undo_data() + var project : Project = Global.current_project + var frame := -1 + var layer := -1 + if Global.animation_timer.is_stopped() and project.selected_cels.size() == 1: + frame = project.current_frame + layer = project.current_layer + + project.undos += 1 + project.undo_redo.create_action(action) + for image in redo_data: + project.undo_redo.add_do_property(image, "data", redo_data[image]) + for image in _undo_data: + project.undo_redo.add_undo_property(image, "data", _undo_data[image]) + project.undo_redo.add_do_method(Global, "redo", frame, layer) + project.undo_redo.add_undo_method(Global, "undo", frame, layer) + project.undo_redo.commit_action() + + _undo_data.clear() + + +func _get_undo_data() -> Dictionary: + var data := {} + var project : Project = Global.current_project + var cels := [] # Array of Cels + if Global.animation_timer.is_stopped(): + for cel_index in project.selected_cels: + cels.append(project.frames[cel_index[0]].cels[cel_index[1]]) + else: + for frame in project.frames: + var cel : Cel = frame.cels[project.current_layer] + cels.append(cel) + for cel in cels: + var image : Image = cel.image + image.unlock() + data[image] = image.data + image.lock() + return data diff --git a/src/Tools/Pencil.gd b/src/Tools/Pencil.gd index 532017310..1e0e7a0e1 100644 --- a/src/Tools/Pencil.gd +++ b/src/Tools/Pencil.gd @@ -103,17 +103,14 @@ func draw_end(_position : Vector2) -> void: if _fill_inside: _draw_points.append(_position) if _draw_points.size() > 3: - var image = _get_draw_image() var v = Vector2() - var image_size = image.get_size() + var image_size = Global.current_project.size for x in image_size.x: v.x = x for y in image_size.y: v.y = y if Geometry.is_point_in_polygon(v, _draw_points): - image.lock() draw_tool(v) - image.unlock() if _changed or _drawer.color_op.changed: commit_undo("Draw") cursor_text = "" @@ -122,9 +119,10 @@ func draw_end(_position : Vector2) -> void: func _draw_brush_image(image : Image, src_rect: Rect2, dst: Vector2) -> void: _changed = true + var images := _get_selected_draw_images() if _overwrite: - _get_draw_image().blit_rect(image, src_rect, dst) + for draw_image in images: + draw_image.blit_rect(image, src_rect, dst) else: - _get_draw_image().blend_rect(image, src_rect, dst) - - + for draw_image in images: + draw_image.blend_rect(image, src_rect, dst) diff --git a/src/UI/Canvas/Canvas.gd b/src/UI/Canvas/Canvas.gd index 99861b242..28ee6ada0 100644 --- a/src/UI/Canvas/Canvas.gd +++ b/src/UI/Canvas/Canvas.gd @@ -78,8 +78,6 @@ func _input(event : InputEvent) -> void: sprite_changed_this_frame = false - var current_project : Project = Global.current_project - if Global.has_focus: if !cursor_image_has_changed: cursor_image_has_changed = true @@ -96,7 +94,7 @@ func _input(event : InputEvent) -> void: Tools.handle_draw(current_pixel.floor(), event) if sprite_changed_this_frame: - update_texture(current_project.current_layer) + update_selected_cels_textures() func camera_zoom() -> void: @@ -231,6 +229,20 @@ func update_texture(layer_index : int, frame_index := -1, project : Project = Gl frame_texture_rect.texture = current_cel.image_texture +func update_selected_cels_textures(project : Project = Global.current_project) -> void: + for cel_index in project.selected_cels: + var frame_index : int = cel_index[0] + var layer_index : int = cel_index[1] + if frame_index < project.frames.size() and layer_index < project.layers.size(): + var current_cel : Cel = project.frames[frame_index].cels[layer_index] + current_cel.image_texture.create_from_image(current_cel.image, 0) + + if project == Global.current_project: + var frame_texture_rect : TextureRect + frame_texture_rect = Global.find_node_by_name(project.layers[layer_index].frame_container.get_child(frame_index), "CelTexture") + frame_texture_rect.texture = current_cel.image_texture + + func onion_skinning() -> void: # Past if Global.onion_skinning_past_rate > 0: diff --git a/src/UI/Timeline/AnimationTimeline.gd b/src/UI/Timeline/AnimationTimeline.gd index a909957bf..85ded1201 100644 --- a/src/UI/Timeline/AnimationTimeline.gd +++ b/src/UI/Timeline/AnimationTimeline.gd @@ -193,10 +193,6 @@ func _on_CopyFrame_pressed(frame := -1) -> void: Global.current_project.undo_redo.add_do_property(Global.current_project, "frames", new_frames) Global.current_project.undo_redo.add_do_property(Global.current_project, "current_frame", frame + 1) Global.current_project.undo_redo.add_do_property(Global.current_project, "animation_tags", new_animation_tags) - for i in range(Global.current_project.layers.size()): - for child in Global.current_project.layers[i].frame_container.get_children(): - Global.current_project.undo_redo.add_do_property(child, "pressed", false) - Global.current_project.undo_redo.add_undo_property(child, "pressed", child.pressed) Global.current_project.undo_redo.add_undo_property(Global.current_project, "frames", Global.current_project.frames) Global.current_project.undo_redo.add_undo_property(Global.current_project, "current_frame", frame) @@ -279,6 +275,7 @@ func _on_AnimationTimer_timeout() -> void: var fps = Global.current_project.fps if animation_forward: if Global.current_project.current_frame < last_frame: + Global.current_project.selected_cels.clear() Global.current_project.current_frame += 1 Global.animation_timer.wait_time = Global.current_project.frames[Global.current_project.current_frame].duration * (1/fps) Global.animation_timer.start() # Change the frame, change the wait time and start a cycle, this is the best way to do it @@ -289,6 +286,7 @@ func _on_AnimationTimer_timeout() -> void: Global.play_backwards.pressed = false Global.animation_timer.stop() 1: # Cycle loop + Global.current_project.selected_cels.clear() Global.current_project.current_frame = first_frame Global.animation_timer.wait_time = Global.current_project.frames[Global.current_project.current_frame].duration * (1/fps) Global.animation_timer.start() @@ -298,6 +296,7 @@ func _on_AnimationTimer_timeout() -> void: else: if Global.current_project.current_frame > first_frame: + Global.current_project.selected_cels.clear() Global.current_project.current_frame -= 1 Global.animation_timer.wait_time = Global.current_project.frames[Global.current_project.current_frame].duration * (1/fps) Global.animation_timer.start() @@ -308,6 +307,7 @@ func _on_AnimationTimer_timeout() -> void: Global.play_forward.pressed = false Global.animation_timer.stop() 1: # Cycle loop + Global.current_project.selected_cels.clear() Global.current_project.current_frame = last_frame Global.animation_timer.wait_time = Global.current_project.frames[Global.current_project.current_frame].duration * (1/fps) Global.animation_timer.start() @@ -355,20 +355,24 @@ func play_animation(play : bool, forward_dir : bool) -> void: func _on_NextFrame_pressed() -> void: + Global.current_project.selected_cels.clear() if Global.current_project.current_frame < Global.current_project.frames.size() - 1: Global.current_project.current_frame += 1 func _on_PreviousFrame_pressed() -> void: + Global.current_project.selected_cels.clear() if Global.current_project.current_frame > 0: Global.current_project.current_frame -= 1 func _on_LastFrame_pressed() -> void: + Global.current_project.selected_cels.clear() Global.current_project.current_frame = Global.current_project.frames.size() - 1 func _on_FirstFrame_pressed() -> void: + Global.current_project.selected_cels.clear() Global.current_project.current_frame = 0 diff --git a/src/UI/Timeline/CelButton.gd b/src/UI/Timeline/CelButton.gd index 2b5ba0b68..e74372ba0 100644 --- a/src/UI/Timeline/CelButton.gd +++ b/src/UI/Timeline/CelButton.gd @@ -43,13 +43,50 @@ func _on_CelButton_resized() -> void: func _on_CelButton_pressed() -> void: + var project := Global.current_project if Input.is_action_just_released("left_mouse"): - Global.current_project.current_frame = frame - Global.current_project.current_layer = layer + var change_cel := true + var prev_curr_frame : int = project.current_frame + var prev_curr_layer : int = project.current_layer + + if Input.is_action_pressed("shift"): + var frame_diff_sign = sign(frame - prev_curr_frame) + if frame_diff_sign == 0: + frame_diff_sign = 1 + var layer_diff_sign = sign(layer - prev_curr_layer) + if layer_diff_sign == 0: + layer_diff_sign = 1 + for i in range(prev_curr_frame, frame + frame_diff_sign, frame_diff_sign): + for j in range(prev_curr_layer, layer + layer_diff_sign, layer_diff_sign): + var frame_layer := [i, j] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + elif Input.is_action_pressed("ctrl"): + var frame_layer := [frame, layer] + if project.selected_cels.has(frame_layer): + if project.selected_cels.size() > 1: + project.selected_cels.erase(frame_layer) + change_cel = false + else: + project.selected_cels.append(frame_layer) + else: # If the button is pressed without Shift or Control + project.selected_cels.clear() + var frame_layer := [frame, layer] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + + if change_cel: + project.current_frame = frame + project.current_layer = layer + else: + project.current_frame = project.selected_cels[0][0] + project.current_layer = project.selected_cels[0][1] + release_focus() + elif Input.is_action_just_released("right_mouse"): popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE)) pressed = !pressed - elif Input.is_action_just_released("middle_mouse"): # Middle mouse click + elif Input.is_action_just_released("middle_mouse"): pressed = !pressed delete_cel_contents() else: # An example of this would be Space @@ -147,6 +184,8 @@ func can_drop_data(_pos, data) -> bool: func drop_data(_pos, data) -> void: var new_frame = data[1] var new_layer = data[2] + if new_frame == frame and new_layer == layer: + return var this_frame_new_cels = Global.current_project.frames[frame].cels.duplicate() var new_frame_new_cels @@ -161,6 +200,9 @@ func drop_data(_pos, data) -> void: Global.current_project.undo_redo.create_action("Move Cels") Global.current_project.undo_redo.add_do_property(Global.current_project.frames[frame], "cels", this_frame_new_cels) + Global.current_project.undo_redo.add_do_property(Global.current_project, "current_layer", layer) + Global.current_project.undo_redo.add_undo_property(Global.current_project, "current_layer", Global.current_project.current_layer) + if frame != new_frame: # If the cel moved to a different frame Global.current_project.undo_redo.add_do_property(Global.current_project.frames[new_frame], "cels", new_frame_new_cels) diff --git a/src/UI/Timeline/FrameButton.gd b/src/UI/Timeline/FrameButton.gd index 07ad64cf6..2e2cb9bb7 100644 --- a/src/UI/Timeline/FrameButton.gd +++ b/src/UI/Timeline/FrameButton.gd @@ -12,7 +12,29 @@ func _ready() -> void: func _button_pressed() -> void: if Input.is_action_just_released("left_mouse"): + var prev_curr_frame : int = Global.current_project.current_frame + if Input.is_action_pressed("shift"): + var frame_diff_sign = sign(frame - prev_curr_frame) + if frame_diff_sign == 0: + frame_diff_sign = 1 + for i in range(prev_curr_frame, frame + frame_diff_sign, frame_diff_sign): + for j in range(0, Global.current_project.layers.size()): + var frame_layer := [i, j] + if !Global.current_project.selected_cels.has(frame_layer): + Global.current_project.selected_cels.append(frame_layer) + elif Input.is_action_pressed("ctrl"): + for j in range(0, Global.current_project.layers.size()): + var frame_layer := [frame, j] + if !Global.current_project.selected_cels.has(frame_layer): + Global.current_project.selected_cels.append(frame_layer) + else: # If the button is pressed without Shift or Control + Global.current_project.selected_cels.clear() + var frame_layer := [frame, Global.current_project.current_layer] + if !Global.current_project.selected_cels.has(frame_layer): + Global.current_project.selected_cels.append(frame_layer) + Global.current_project.current_frame = frame + elif Input.is_action_just_released("right_mouse"): if Global.current_project.frames.size() == 1: popup_menu.set_item_disabled(0, true) @@ -26,7 +48,7 @@ func _button_pressed() -> void: popup_menu.set_item_disabled(3, false) popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ONE)) pressed = !pressed - elif Input.is_action_just_released("middle_mouse"): # Middle mouse click + elif Input.is_action_just_released("middle_mouse"): pressed = !pressed Global.animation_timeline._on_DeleteFrame_pressed(frame) else: # An example of this would be Space @@ -59,12 +81,14 @@ func change_frame_order(rate : int) -> void: Global.current_project.undo_redo.create_action("Change Frame Order") Global.current_project.undo_redo.add_do_property(Global.current_project, "frames", new_frames) + Global.current_project.undo_redo.add_undo_property(Global.current_project, "frames", Global.current_project.frames) if Global.current_project.current_frame == frame: Global.current_project.undo_redo.add_do_property(Global.current_project, "current_frame", change) - Global.current_project.undo_redo.add_undo_property(Global.current_project, "current_frame", Global.current_project.current_frame) + else: + Global.current_project.undo_redo.add_do_property(Global.current_project, "current_frame", Global.current_project.current_frame) - Global.current_project.undo_redo.add_undo_property(Global.current_project, "frames", Global.current_project.frames) + Global.current_project.undo_redo.add_undo_property(Global.current_project, "current_frame", Global.current_project.current_frame) Global.current_project.undo_redo.add_undo_method(Global, "undo") Global.current_project.undo_redo.add_do_method(Global, "redo") diff --git a/src/UI/Timeline/LayerButton.gd b/src/UI/Timeline/LayerButton.gd index dca725b1c..f339ffa39 100644 --- a/src/UI/Timeline/LayerButton.gd +++ b/src/UI/Timeline/LayerButton.gd @@ -45,9 +45,33 @@ func _input(event : InputEvent) -> void: save_layer_name(line_edit.text) -func _on_LayerContainer_gui_input(event : InputEvent): +func _on_LayerContainer_gui_input(event : InputEvent) -> void: + var project := Global.current_project + if event is InputEventMouseButton: - Global.current_project.current_layer = layer + var prev_curr_layer : int = project.current_layer + if Input.is_action_pressed("shift"): + var layer_diff_sign = sign(layer - prev_curr_layer) + if layer_diff_sign == 0: + layer_diff_sign = 1 + for i in range(0, project.frames.size()): + for j in range(prev_curr_layer, layer + layer_diff_sign, layer_diff_sign): + var frame_layer := [i, j] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + elif Input.is_action_pressed("ctrl"): + for i in range(0, project.frames.size()): + var frame_layer := [i, layer] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + else: # If the button is pressed without Shift or Control + project.selected_cels.clear() + var frame_layer := [project.current_frame, layer] + if !project.selected_cels.has(frame_layer): + project.selected_cels.append(frame_layer) + + project.current_layer = layer + if event.doubleclick: label.visible = false line_edit.visible = true diff --git a/src/UI/Timeline/LayerButton.tscn b/src/UI/Timeline/LayerButton.tscn index bb14ca1d7..c0c001e66 100644 --- a/src/UI/Timeline/LayerButton.tscn +++ b/src/UI/Timeline/LayerButton.tscn @@ -9,6 +9,7 @@ margin_right = 210.0 margin_bottom = 36.0 rect_min_size = Vector2( 212, 36 ) +mouse_default_cursor_shape = 2 size_flags_horizontal = 0 toggle_mode = true action_mode = 0