From dd8145d369dc4878270149050f2d586db0c8b503 Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas <35376950+OverloadedOrama@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:24:41 +0200 Subject: [PATCH] Greatly simplify backup code, got rid of OpenSave's `current_save_paths` and `backup_save_paths` The previous backup code was unnecessarily complicated, hard to read/understand and prone to errors. The new system simply stores the save and backup paths in the `Project` class, and stores the backup files inside `user://backups`, instead of having their file paths be in `cache.ini`. --- src/Autoload/OpenSave.gd | 102 ++++---------------- src/Classes/Project.gd | 14 ++- src/Main.gd | 52 ++++------ src/Main.tscn | 1 - src/UI/Tabs.gd | 11 --- src/UI/TopMenuContainer/TopMenuContainer.gd | 2 +- 6 files changed, 51 insertions(+), 131 deletions(-) diff --git a/src/Autoload/OpenSave.gd b/src/Autoload/OpenSave.gd index b8e9ff5a4..7be6ae0fc 100644 --- a/src/Autoload/OpenSave.gd +++ b/src/Autoload/OpenSave.gd @@ -4,9 +4,6 @@ extends Node signal project_saved signal reference_image_imported -var current_save_paths: PackedStringArray = [] -## Stores a filename of a backup file in user:// until user saves manually -var backup_save_paths: PackedStringArray = [] var preview_dialog_tscn := preload("res://src/UI/Dialogs/ImportPreviewDialog.tscn") var preview_dialogs := [] ## Array of preview dialogs var last_dialog_option := 0 @@ -253,9 +250,11 @@ func open_pxo_file(path: String, is_backup := false, replace_empty := true) -> v Global.tabs.current_tab = Global.tabs.get_tab_count() - 1 Global.canvas.camera_zoom() - if not is_backup: + if is_backup: + new_project.backup_path = path + else: # Loading a backup should not change window title and save path - current_save_paths[Global.current_project_index] = path + new_project.save_path = path Global.main_window.title = path.get_file() + " - Pixelorama " + Global.current_version Global.save_sprites_dialog.current_path = path # Set last opened project path and save @@ -366,8 +365,8 @@ func save_pxo_file( zip_packer.write_file(to_save.to_utf8_buffer()) zip_packer.close_file() - if !autosave: - current_save_paths[Global.current_project_index] = path + if not autosave: + project.save_path = path var frame_index := 1 for frame in project.frames: @@ -417,7 +416,7 @@ func save_pxo_file( # First remove backup then set current save path if project.has_changed: project.has_changed = false - remove_backup(Global.current_project_index) + Global.current_project.remove_backup_file() Global.notification_label("File saved") Global.main_window.title = path.get_file() + " - Pixelorama " + Global.current_version @@ -809,83 +808,22 @@ func update_autosave() -> void: func _on_Autosave_timeout() -> void: - for i in range(backup_save_paths.size()): - if backup_save_paths[i] == "": - # Create a new backup file if it doesn't exist yet - backup_save_paths[i] = ( - "user://backup-" + str(Time.get_unix_time_from_system()) + "-%s" % i + for i in Global.projects.size(): + var project := Global.projects[i] + if project.backup_path.is_empty(): + project.backup_path = ( + "user://backups/backup-" + str(Time.get_unix_time_from_system()) + "-%s" % i ) - - store_backup_path(i) - save_pxo_file(backup_save_paths[i], true, false, Global.projects[i]) + save_pxo_file(project.backup_path, true, false, project) -## Backup paths are stored in two ways: -## 1) User already manually saved and defined a save path -> {current_save_path, backup_save_path} -## 2) User didn't manually save, "untitled" backup is stored -> {backup_save_path, backup_save_path} -func store_backup_path(i: int) -> void: - if current_save_paths[i] != "": - # Remove "untitled" backup if it existed on this project instance - if Global.config_cache.has_section_key("backups", backup_save_paths[i]): - Global.config_cache.erase_section_key("backups", backup_save_paths[i]) - - Global.config_cache.set_value("backups", current_save_paths[i], backup_save_paths[i]) - else: - Global.config_cache.set_value("backups", backup_save_paths[i], backup_save_paths[i]) - - Global.config_cache.save("user://cache.ini") - - -func remove_backup(i: int) -> void: - # Remove backup file - if backup_save_paths[i] != "": - if current_save_paths[i] != "": - remove_backup_by_path(current_save_paths[i], backup_save_paths[i]) - else: - # If manual save was not yet done - remove "untitled" backup - remove_backup_by_path(backup_save_paths[i], backup_save_paths[i]) - backup_save_paths[i] = "" - - -func remove_backup_by_path(project_path: String, backup_path: String) -> void: - DirAccess.open("user://").remove(backup_path) - if Global.config_cache.has_section_key("backups", project_path): - Global.config_cache.erase_section_key("backups", project_path) - elif Global.config_cache.has_section_key("backups", backup_path): - Global.config_cache.erase_section_key("backups", backup_path) - Global.config_cache.save("user://cache.ini") - - -func reload_backup_file(project_paths: Array, backup_paths: Array) -> void: - assert(project_paths.size() == backup_paths.size()) - # Clear non-existent backups - var existing_backups_count := 0 - for i in range(backup_paths.size()): - var dir := DirAccess.open("user://") - if dir.file_exists(backup_paths[i]): - project_paths[existing_backups_count] = project_paths[i] - backup_paths[existing_backups_count] = backup_paths[i] - existing_backups_count += 1 - else: - if Global.config_cache.has_section_key("backups", backup_paths[i]): - Global.config_cache.erase_section_key("backups", backup_paths[i]) - Global.config_cache.save("user://cache.ini") - project_paths.resize(existing_backups_count) - backup_paths.resize(existing_backups_count) - - # Load the backup files - for i in range(project_paths.size()): - open_pxo_file(backup_paths[i], true, i == 0) - backup_save_paths[i] = backup_paths[i] - - # If project path is the same as backup save path -> the backup was untitled - if project_paths[i] != backup_paths[i]: # If the user has saved - current_save_paths[i] = project_paths[i] - Global.main_window.title = ( - project_paths[i].get_file() + " - Pixelorama(*) " + Global.current_version - ) - Global.current_project.has_changed = true - +## Load the backup files +func reload_backup_file() -> void: + var dir := DirAccess.open("user://backups") + var i := 0 + for file in dir.get_files(): + open_pxo_file("user://backups".path_join(file), true, i == 0) + i += 1 Global.notification_label("Backup reloaded") diff --git a/src/Classes/Project.gd b/src/Classes/Project.gd index c8434d235..431bdfc26 100644 --- a/src/Classes/Project.gd +++ b/src/Classes/Project.gd @@ -73,11 +73,13 @@ var cameras_zoom: PackedVector2Array = [ var cameras_offset: PackedVector2Array = [Vector2.ZERO, Vector2.ZERO, Vector2.ZERO] # Export directory path and export file name +var save_path := "" var export_directory_path := "" var file_name := "untitled" var file_format := Export.FileFormat.PNG var was_exported := false var export_overwrite := false +var backup_path := "" var animation_tag_node := preload("res://src/UI/Timeline/AnimationTagUI.tscn") @@ -88,20 +90,15 @@ func _init(_frames: Array[Frame] = [], _name := tr("untitled"), _size := Vector2 size = _size tiles = Tiles.new(size) selection_map.copy_from(Image.create(size.x, size.y, false, Image.FORMAT_LA8)) - Global.tabs.add_tab(name) - OpenSave.current_save_paths.append("") - OpenSave.backup_save_paths.append("") x_symmetry_point = size.x - 1 y_symmetry_point = size.y - 1 - x_symmetry_axis.type = x_symmetry_axis.Types.HORIZONTAL x_symmetry_axis.project = self x_symmetry_axis.add_point(Vector2(-19999, y_symmetry_point / 2 + 0.5)) x_symmetry_axis.add_point(Vector2(19999, y_symmetry_point / 2 + 0.5)) Global.canvas.add_child(x_symmetry_axis) - y_symmetry_axis.type = y_symmetry_axis.Types.VERTICAL y_symmetry_axis.project = self y_symmetry_axis.add_point(Vector2(x_symmetry_point / 2 + 0.5, -19999)) @@ -118,6 +115,7 @@ func _init(_frames: Array[Frame] = [], _name := tr("untitled"), _size := Vector2 func remove() -> void: + remove_backup_file() undo_redo.free() for ri in reference_images: ri.queue_free() @@ -138,6 +136,12 @@ func remove() -> void: Global.projects.erase(self) +func remove_backup_file() -> void: + if not backup_path.is_empty(): + if FileAccess.file_exists(backup_path): + DirAccess.remove_absolute(backup_path) + + func commit_undo() -> void: if not can_undo: return diff --git a/src/Main.gd b/src/Main.gd index 55f5f2e87..33448b1f3 100644 --- a/src/Main.gd +++ b/src/Main.gd @@ -24,6 +24,8 @@ var splash_dialog: AcceptDialog: func _init() -> void: + if not DirAccess.dir_exists_absolute("user://backups"): + DirAccess.make_dir_recursive_absolute("user://backups") Global.shrink = _get_auto_display_scale() _handle_layout_files() @@ -171,27 +173,14 @@ func _show_splash_screen() -> void: func _handle_backup() -> void: # If backup file exists, Pixelorama was not closed properly (probably crashed) - reopen backup backup_confirmation.add_button("Discard All", false, "discard") - if Global.config_cache.has_section("backups"): - var project_paths = Global.config_cache.get_section_keys("backups") - if project_paths.size() > 0: - # Get backup paths - var backup_paths := [] - for p_path in project_paths: - backup_paths.append(Global.config_cache.get_value("backups", p_path)) - # Temporatily stop autosave until user confirms backup - OpenSave.autosave_timer.stop() - backup_confirmation.confirmed.connect( - _on_BackupConfirmation_confirmed.bind(project_paths, backup_paths) - ) - backup_confirmation.custom_action.connect( - _on_BackupConfirmation_custom_action.bind(project_paths, backup_paths) - ) - await get_tree().process_frame - backup_confirmation.popup_centered() - modulate = Color(0.5, 0.5, 0.5) - else: - if Global.open_last_project: - load_last_project() + var backup_dir := DirAccess.open("user://backups") + if backup_dir.get_files().size() > 0: + # Temporatily stop autosave until user confirms backup + OpenSave.autosave_timer.stop() + backup_confirmation.confirmed.connect(_on_BackupConfirmation_confirmed) + backup_confirmation.custom_action.connect(_on_BackupConfirmation_custom_action) + backup_confirmation.popup_centered() + modulate = Color(0.5, 0.5, 0.5) else: if Global.open_last_project: load_last_project() @@ -367,18 +356,15 @@ func _quit() -> void: get_tree().quit() -func _on_BackupConfirmation_confirmed(project_paths: Array, backup_paths: Array) -> void: - OpenSave.reload_backup_file(project_paths, backup_paths) +func _on_BackupConfirmation_confirmed() -> void: + OpenSave.reload_backup_file() -func _on_BackupConfirmation_custom_action( - action: String, project_paths: Array, backup_paths: Array -) -> void: +func _on_BackupConfirmation_custom_action(action: String) -> void: backup_confirmation.hide() if action != "discard": return - for i in range(project_paths.size()): - OpenSave.remove_backup_by_path(project_paths[i], backup_paths[i]) + _clear_backup_files() # Reopen last project if Global.open_last_project: load_last_project() @@ -392,6 +378,11 @@ func _on_backup_confirmation_visibility_changed() -> void: Global.dialog_open(false) +func _clear_backup_files() -> void: + for file in DirAccess.get_files_at("user://backups"): + DirAccess.remove_absolute("user://backups".path_join(file)) + + func _exit_tree() -> void: Global.config_cache.set_value("window", "layout", Global.layouts.find(main_ui.layout)) Global.config_cache.set_value("window", "screen", get_window().current_screen) @@ -415,8 +406,7 @@ func _exit_tree() -> void: Global.config_cache.set_value("view_menu", "show_mouse_guides", Global.show_mouse_guides) Global.config_cache.save("user://cache.ini") - var i := 0 for project in Global.projects: project.remove() - OpenSave.remove_backup(i) - i += 1 + # For some reason, the above is not enough to remove all backup files + _clear_backup_files() diff --git a/src/Main.tscn b/src/Main.tscn index 924ce47c5..49fd2b34a 100644 --- a/src/Main.tscn +++ b/src/Main.tscn @@ -77,7 +77,6 @@ popup_window = true dialog_text = "This is an error message!" [node name="BackupConfirmation" type="ConfirmationDialog" parent="Dialogs"] -exclusive = false popup_window = true dialog_text = "Autosaved project(s) from a crashed session were found. Do you want to recover the data?" diff --git a/src/UI/Tabs.gd b/src/UI/Tabs.gd index 084fd8fbd..a5260d6c3 100644 --- a/src/UI/Tabs.gd +++ b/src/UI/Tabs.gd @@ -47,21 +47,10 @@ func _on_active_tab_rearranged(idx_to: int) -> void: Global.projects.erase(temp) Global.projects.insert(idx_to, temp) - # Change save paths - var temp_save_path := OpenSave.current_save_paths[Global.current_project_index] - OpenSave.current_save_paths.remove_at(Global.current_project_index) - OpenSave.current_save_paths.insert(idx_to, temp_save_path) - var temp_backup_path := OpenSave.backup_save_paths[Global.current_project_index] - OpenSave.backup_save_paths.remove_at(Global.current_project_index) - OpenSave.backup_save_paths.insert(idx_to, temp_backup_path) - func delete_tab(tab: int) -> void: remove_tab(tab) Global.projects[tab].remove() - OpenSave.remove_backup(tab) - OpenSave.current_save_paths.remove_at(tab) - OpenSave.backup_save_paths.remove_at(tab) if Global.current_project_index == tab: if tab > 0: Global.current_project_index -= 1 diff --git a/src/UI/TopMenuContainer/TopMenuContainer.gd b/src/UI/TopMenuContainer/TopMenuContainer.gd index 773428ebc..bb5f22e51 100644 --- a/src/UI/TopMenuContainer/TopMenuContainer.gd +++ b/src/UI/TopMenuContainer/TopMenuContainer.gd @@ -461,7 +461,7 @@ func _on_open_last_project_file_menu_option_pressed() -> void: func _save_project_file() -> void: - var path: String = OpenSave.current_save_paths[Global.current_project_index] + var path: String = Global.current_project.save_path if path == "": Global.control.show_save_dialog() else: