diff --git a/Translations/Translations.pot b/Translations/Translations.pot index d8050a5d6..da94d7c2a 100644 --- a/Translations/Translations.pot +++ b/Translations/Translations.pot @@ -124,6 +124,9 @@ msgstr "" msgid "Paste" msgstr "" +msgid "Paste in Place" +msgstr "" + msgid "Delete" msgstr "" diff --git a/project.godot b/project.godot index bef07f422..9fd116fb0 100644 --- a/project.godot +++ b/project.godot @@ -850,6 +850,11 @@ gradient_map={ "deadzone": 0.5, "events": [ ] } +paste_in_place={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":86,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} [locale] diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd index 9faa5aef0..bb5a885b3 100644 --- a/src/Autoload/Global.gd +++ b/src/Autoload/Global.gd @@ -8,7 +8,7 @@ enum ColorFrom { THEME, CUSTOM } enum ButtonSize { SMALL, BIG } enum FileMenu { NEW, OPEN, OPEN_LAST_PROJECT, RECENT, SAVE, SAVE_AS, EXPORT, EXPORT_AS, QUIT } -enum EditMenu { UNDO, REDO, COPY, CUT, PASTE, DELETE, NEW_BRUSH, PREFERENCES } +enum EditMenu { UNDO, REDO, COPY, CUT, PASTE, PASTE_IN_PLACE, DELETE, NEW_BRUSH, PREFERENCES } enum ViewMenu { TILE_MODE, TILE_MODE_OFFSETS, @@ -253,6 +253,8 @@ func _initialize_keychain() -> void: "cut": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.CUT), "copy": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.COPY), "paste": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.PASTE), + "paste_in_place": + Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.PASTE_IN_PLACE), "delete": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.DELETE), "new_brush": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.NEW_BRUSH), diff --git a/src/UI/Canvas/Selection.gd b/src/UI/Canvas/Selection.gd index 7f0576bd6..bdcca983d 100644 --- a/src/UI/Canvas/Selection.gd +++ b/src/UI/Canvas/Selection.gd @@ -3,6 +3,7 @@ extends Node2D enum SelectionOperation { ADD, SUBTRACT, INTERSECT } const KEY_MOVE_ACTION_NAMES := ["ui_up", "ui_down", "ui_left", "ui_right"] +const CLIPBOARD_FILE_PATH := "user://clipboard.txt" var is_moving_content := false var arrow_key_move := false @@ -682,10 +683,10 @@ func copy() -> void: to_copy = image.get_rect(big_bounding_rectangle) to_copy.lock() # Remove unincluded pixels if the selection is not a single rectangle + var offset_pos := big_bounding_rectangle.position for x in to_copy.get_size().x: for y in to_copy.get_size().y: var pos := Vector2(x, y) - var offset_pos = big_bounding_rectangle.position if offset_pos.x < 0: offset_pos.x = 0 if offset_pos.y < 0: @@ -704,9 +705,9 @@ func copy() -> void: "big_bounding_rectangle": cl_big_bounding_rectangle, "selection_offset": cl_selection_offset, } - # Store to ".clipboard.txt" file + var clipboard_file := File.new() - clipboard_file.open("user://clipboard.txt", File.WRITE) + clipboard_file.open(CLIPBOARD_FILE_PATH, File.WRITE) clipboard_file.store_var(transfer_clipboard, true) clipboard_file.close() @@ -719,53 +720,66 @@ func copy() -> void: container.get_child(0).get_child(0).texture = tex -func paste() -> void: - # Read from the ".clipboard.txt" file +func paste(in_place := false) -> void: var clipboard_file := File.new() - if !clipboard_file.file_exists("user://clipboard.txt"): + if !clipboard_file.file_exists(CLIPBOARD_FILE_PATH): return - clipboard_file.open("user://clipboard.txt", File.READ) + clipboard_file.open(CLIPBOARD_FILE_PATH, File.READ) var clipboard = clipboard_file.get_var(true) clipboard_file.close() - if typeof(clipboard) == TYPE_DICTIONARY: - # A sanity check - if not clipboard.has_all( - ["image", "selection_map", "big_bounding_rectangle", "selection_offset"] - ): - return + # Sanity checks + if typeof(clipboard) != TYPE_DICTIONARY: + return + if !clipboard.has_all(["image", "selection_map", "big_bounding_rectangle", "selection_offset"]): + return + if clipboard.image.is_empty(): + return - if clipboard.image.is_empty(): - return - clear_selection() - undo_data = get_undo_data(true) - var project: Project = Global.current_project + clear_selection() + undo_data = get_undo_data(true) + var project: Project = Global.current_project - original_bitmap.copy_from(project.selection_map) - original_big_bounding_rectangle = big_bounding_rectangle - original_offset = project.selection_offset + original_bitmap.copy_from(project.selection_map) + original_big_bounding_rectangle = big_bounding_rectangle + original_offset = project.selection_offset - var clip_map := SelectionMap.new() - clip_map.data = clipboard.selection_map - var max_size := Vector2( - max(clip_map.get_size().x, project.selection_map.get_size().x), - max(clip_map.get_size().y, project.selection_map.get_size().y) - ) + var clip_map := SelectionMap.new() + clip_map.data = clipboard.selection_map + var max_size := Vector2( + max(clip_map.get_size().x, project.selection_map.get_size().x), + max(clip_map.get_size().y, project.selection_map.get_size().y) + ) - project.selection_map = clip_map - project.selection_map.crop(max_size.x, max_size.y) - self.big_bounding_rectangle = clipboard.big_bounding_rectangle - project.selection_offset = clipboard.selection_offset + project.selection_map = clip_map + project.selection_map.crop(max_size.x, max_size.y) + project.selection_offset = clipboard.selection_offset + big_bounding_rectangle = clipboard.big_bounding_rectangle + if not in_place: # If "Paste" is selected, and not "Paste in Place" + var camera_center := Global.camera.get_camera_screen_center() + camera_center -= big_bounding_rectangle.size / 2 + var max_pos := project.size - big_bounding_rectangle.size + if max_pos.x >= 0: + camera_center.x = clamp(camera_center.x, 0, max_pos.x) + else: + camera_center.x = 0 + if max_pos.y >= 0: + camera_center.y = clamp(camera_center.y, 0, max_pos.y) + else: + camera_center.y = 0 + big_bounding_rectangle.position = camera_center + project.selection_map.move_bitmap_values(Global.current_project, false) - temp_bitmap = project.selection_map - temp_rect = big_bounding_rectangle - is_moving_content = true - is_pasting = true - original_preview_image = clipboard.image - preview_image.copy_from(original_preview_image) - preview_image_texture.create_from_image(preview_image, 0) + self.big_bounding_rectangle = big_bounding_rectangle + temp_bitmap = project.selection_map + temp_rect = big_bounding_rectangle + is_moving_content = true + is_pasting = true + original_preview_image = clipboard.image + preview_image.copy_from(original_preview_image) + preview_image_texture.create_from_image(preview_image, 0) - project.selection_map_changed() + project.selection_map_changed() func delete(selected_cels := true) -> void: diff --git a/src/UI/TopMenuContainer.gd b/src/UI/TopMenuContainer.gd index 4cd34b612..093e973d0 100644 --- a/src/UI/TopMenuContainer.gd +++ b/src/UI/TopMenuContainer.gd @@ -89,7 +89,15 @@ func update_recent_projects_submenu() -> void: func _setup_edit_menu() -> void: # Order as in Global.EditMenu enum var edit_menu_items := [ - "Undo", "Redo", "Copy", "Cut", "Paste", "Delete", "New Brush", "Preferences" + "Undo", + "Redo", + "Copy", + "Cut", + "Paste", + "Paste in Place", + "Delete", + "New Brush", + "Preferences" ] var edit_menu: PopupMenu = edit_menu_button.get_popup() var i := 0 @@ -401,6 +409,8 @@ func edit_menu_id_pressed(id: int) -> void: Global.canvas.selection.cut() Global.EditMenu.PASTE: Global.canvas.selection.paste() + Global.EditMenu.PASTE_IN_PLACE: + Global.canvas.selection.paste(true) Global.EditMenu.DELETE: Global.canvas.selection.delete() Global.EditMenu.NEW_BRUSH: