diff --git a/src/Classes/Project.gd b/src/Classes/Project.gd index fffdfcd39..0143baa36 100644 --- a/src/Classes/Project.gd +++ b/src/Classes/Project.gd @@ -630,14 +630,18 @@ func remove_frames(indices: Array) -> void: # indices should be in ascending or _update_frame_ui() -func move_frame(from_index: int, to_index: int) -> void: +# from_indices and to_indicies should be in ascending order +func move_frames(from_indices: PackedInt32Array, to_indices: PackedInt32Array) -> void: Global.canvas.selection.transform_content_confirm() selected_cels.clear() - var frame := frames[from_index] - frames.remove_at(from_index) - Global.animation_timeline.project_frame_removed(from_index) - frames.insert(to_index, frame) - Global.animation_timeline.project_frame_added(to_index) + var removed_frames := [] + for i in from_indices.size(): + # With each removed index, future indices need to be lowered, so subtract by i + removed_frames.append(frames.pop_at(from_indices[i] - i)) + Global.animation_timeline.project_frame_removed(from_indices[i] - i) + for i in to_indices.size(): + frames.insert(to_indices[i], removed_frames[i]) + Global.animation_timeline.project_frame_added(to_indices[i]) _update_frame_ui() diff --git a/src/UI/Timeline/AnimationTimeline.gd b/src/UI/Timeline/AnimationTimeline.gd index 28b29912b..65c9ed3e2 100644 --- a/src/UI/Timeline/AnimationTimeline.gd +++ b/src/UI/Timeline/AnimationTimeline.gd @@ -509,13 +509,46 @@ func _on_FrameTagButton_pressed() -> void: func _on_MoveLeft_pressed() -> void: if Global.current_project.current_frame == 0: return - Global.frame_hbox.get_child(Global.current_project.current_frame).change_frame_order(-1) + move_frames(Global.current_project.current_frame, -1) func _on_MoveRight_pressed() -> void: if Global.current_project.current_frame == Global.current_project.frames.size() - 1: return - Global.frame_hbox.get_child(Global.current_project.current_frame).change_frame_order(1) + move_frames(Global.current_project.current_frame, 1) + + +func move_frames(frame: int, rate: int) -> void: + var project := Global.current_project + var frame_indices: PackedInt32Array = [] + var moved_frame_indices: PackedInt32Array = [] + for cel in project.selected_cels: + var frame_index: int = cel[0] + if not frame_indices.has(frame_index): + frame_indices.append(frame_index) + moved_frame_indices.append(frame_index + rate) + frame_indices.sort() + moved_frame_indices.sort() + if not frame in frame_indices: + frame_indices = [frame] + moved_frame_indices = [frame + rate] + for moved_index in moved_frame_indices: + # Don't allow frames to be moved if they are out of bounds + if moved_index < 0 or moved_index >= project.frames.size(): + return + project.undo_redo.create_action("Change Frame Order") + project.undo_redo.add_do_method(project.move_frames.bind(frame_indices, moved_frame_indices)) + project.undo_redo.add_undo_method(project.move_frames.bind(moved_frame_indices, frame_indices)) + + if project.current_frame in frame_indices: + project.undo_redo.add_do_method(project.change_cel.bind(frame + rate)) + else: + project.undo_redo.add_do_method(project.change_cel.bind(project.current_frame)) + + project.undo_redo.add_undo_method(project.change_cel.bind(project.current_frame)) + project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true)) + project.undo_redo.add_do_method(Global.undo_or_redo.bind(false)) + project.undo_redo.commit_action() func reverse_frames(indices := []) -> void: diff --git a/src/UI/Timeline/FrameButton.gd b/src/UI/Timeline/FrameButton.gd index 16ab7e660..d64db5e57 100644 --- a/src/UI/Timeline/FrameButton.gd +++ b/src/UI/Timeline/FrameButton.gd @@ -84,9 +84,9 @@ func _on_PopupMenu_id_pressed(id: int) -> void: CLONE: Global.animation_timeline.copy_frames(indices) MOVE_LEFT: - change_frame_order(-1) + Global.animation_timeline.move_frames(frame, -1) MOVE_RIGHT: - change_frame_order(1) + Global.animation_timeline.move_frames(frame, 1) PROPERTIES: frame_properties.frame_indices = indices frame_properties.popup_centered() @@ -97,25 +97,6 @@ func _on_PopupMenu_id_pressed(id: int) -> void: DrawingAlgos.center(indices) -func change_frame_order(rate: int) -> void: - var change := frame + rate - var project := Global.current_project - - project.undo_redo.create_action("Change Frame Order") - project.undo_redo.add_do_method(project.move_frame.bind(frame, change)) - project.undo_redo.add_undo_method(project.move_frame.bind(change, frame)) - - if project.current_frame == frame: - project.undo_redo.add_do_method(project.change_cel.bind(change)) - else: - project.undo_redo.add_do_method(project.change_cel.bind(project.current_frame)) - - project.undo_redo.add_undo_method(project.change_cel.bind(project.current_frame)) - project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true)) - project.undo_redo.add_do_method(Global.undo_or_redo.bind(false)) - project.undo_redo.commit_action() - - func _get_drag_data(_position: Vector2) -> Variant: var button := Button.new() button.size = size @@ -123,50 +104,55 @@ func _get_drag_data(_position: Vector2) -> Variant: button.text = text set_drag_preview(button) - return ["Frame", frame] + return ["Frame", _get_frame_indices()] func _can_drop_data(_pos: Vector2, data) -> bool: - if typeof(data) == TYPE_ARRAY: - if data[0] == "Frame": - if data[1] != frame: # Can't move to same frame - var region: Rect2 - if Input.is_action_pressed("ctrl"): # Swap frames - region = get_global_rect() - else: # Move frames - if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): - region = _get_region_rect(-0.125, 0.125) - region.position.x -= 2 # Container spacing - else: - region = _get_region_rect(0.875, 1.125) - region.position.x += 2 # Container spacing - Global.animation_timeline.drag_highlight.global_position = region.position - Global.animation_timeline.drag_highlight.size = region.size - Global.animation_timeline.drag_highlight.visible = true - return true - Global.animation_timeline.drag_highlight.visible = false - return false + if typeof(data) != TYPE_ARRAY: + Global.animation_timeline.drag_highlight.visible = false + return false + if data[0] != "Frame": + Global.animation_timeline.drag_highlight.visible = false + return false + var drop_frames: PackedInt32Array = data[1] + # Can't move to same frame + if drop_frames[-1] == frame: + Global.animation_timeline.drag_highlight.visible = false + return false + var region: Rect2 + if Input.is_action_pressed("ctrl") and drop_frames.size() == 1: # Swap frames + region = get_global_rect() + else: # Move frames + if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): + region = _get_region_rect(-0.125, 0.125) + else: + region = _get_region_rect(0.875, 1.125) + Global.animation_timeline.drag_highlight.global_position = region.position + Global.animation_timeline.drag_highlight.size = region.size + Global.animation_timeline.drag_highlight.visible = true + return true func _drop_data(_pos: Vector2, data) -> void: - var drop_frame: int = data[1] + var drop_frames: PackedInt32Array = data[1] var project := Global.current_project project.undo_redo.create_action("Change Frame Order") - if Input.is_action_pressed("ctrl"): # Swap frames - project.undo_redo.add_do_method(project.swap_frame.bind(frame, drop_frame)) - project.undo_redo.add_undo_method(project.swap_frame.bind(frame, drop_frame)) + if Input.is_action_pressed("ctrl") and drop_frames.size() == 1: # Swap frames + project.undo_redo.add_do_method(project.swap_frame.bind(frame, drop_frames[0])) + project.undo_redo.add_undo_method(project.swap_frame.bind(frame, drop_frames[0])) else: # Move frames var to_frame: int if _get_region_rect(0, 0.5).has_point(get_global_mouse_position()): # Left to_frame = frame else: # Right to_frame = frame + 1 - if drop_frame < frame: - to_frame -= 1 - project.undo_redo.add_do_method(project.move_frame.bind(drop_frame, to_frame)) - project.undo_redo.add_undo_method(project.move_frame.bind(to_frame, drop_frame)) + if drop_frames[-1] < frame: + to_frame -= drop_frames.size() + var to_frames := range(to_frame, to_frame + drop_frames.size()) + project.undo_redo.add_do_method(project.move_frames.bind(drop_frames, to_frames)) + project.undo_redo.add_undo_method(project.move_frames.bind(to_frames, drop_frames)) - if project.current_frame == drop_frame: + if project.current_frame in drop_frames: project.undo_redo.add_do_method(project.change_cel.bind(frame)) else: project.undo_redo.add_do_method(project.change_cel.bind(project.current_frame))